summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--REUSE.toml2
-rw-r--r--src/core/api/qwebenginepage.cpp79
-rw-r--r--src/core/api/qwebenginepage_p.h6
-rw-r--r--src/core/api/qwebenginepermission.cpp39
-rw-r--r--src/core/api/qwebenginepermission_p.h6
-rw-r--r--src/core/api/qwebengineprofile.cpp2
-rw-r--r--src/core/media_capture_devices_dispatcher.cpp30
-rw-r--r--src/core/permission_manager_qt.cpp435
-rw-r--r--src/core/permission_manager_qt.h37
-rw-r--r--src/core/profile_adapter.cpp24
-rw-r--r--src/core/profile_adapter.h5
-rw-r--r--src/core/web_contents_adapter.cpp158
-rw-r--r--src/core/web_contents_adapter.h12
-rw-r--r--src/core/web_contents_adapter_client.h7
-rw-r--r--src/core/web_contents_delegate_qt.cpp48
-rw-r--r--src/core/web_contents_delegate_qt.h3
-rw-r--r--src/webenginequick/api/qquickwebengineprofile.cpp2
-rw-r--r--src/webenginequick/api/qquickwebengineprofile.h2
-rw-r--r--src/webenginequick/api/qquickwebengineview.cpp82
-rw-r--r--src/webenginequick/api/qquickwebengineview_p_p.h6
-rw-r--r--tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp2
-rw-r--r--tests/auto/quick/qmltests/data/tst_getUserMedia.qml13
-rw-r--r--tests/auto/util/util.h7
-rw-r--r--tests/auto/widgets/CMakeLists.txt1
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp417
-rw-r--r--tests/auto/widgets/qwebenginepermission/CMakeLists.txt28
-rw-r--r--tests/auto/widgets/qwebenginepermission/resources/iframe.html5
-rw-r--r--tests/auto/widgets/qwebenginepermission/resources/index.html14
-rw-r--r--tests/auto/widgets/qwebenginepermission/resources/qt144.pngbin0 -> 8315 bytes
-rw-r--r--tests/auto/widgets/qwebenginepermission/tst_qwebenginepermission.cpp727
-rw-r--r--tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp191
-rw-r--r--tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp12
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp28
33 files changed, 1389 insertions, 1041 deletions
diff --git a/REUSE.toml b/REUSE.toml
index 19f9b97b3..075c0b340 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -24,6 +24,7 @@ path = ["tests/auto/widgets/qwebenginepage/resources/*",
"tests/auto/widgets/qwebengineview/resources/*",
"tests/auto/widgets/qwebengineprofile/resources/*",
"tests/auto/widgets/qwebengineprofilebuilder/resources/*",
+ "tests/auto/widgets/qwebenginepermission/resources/*",
"tests/auto/widgets/qwebenginehistory/resources/*",
"tests/auto/core/certificateerror/resources/*",
"tests/auto/core/origins/resources/subdir/*",
@@ -121,4 +122,3 @@ precedence = "override"
comment = "License file."
SPDX-FileCopyrightText = "None"
SPDX-License-Identifier = "CC0-1.0"
-
diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp
index bfa184e5c..8d73723f6 100644
--- a/src/core/api/qwebenginepage.cpp
+++ b/src/core/api/qwebenginepage.cpp
@@ -590,49 +590,6 @@ void QWebEnginePagePrivate::showColorDialog(QSharedPointer<ColorChooserControlle
view->showColorDialog(controller);
}
-void QWebEnginePagePrivate::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags)
-{
- Q_Q(QWebEnginePage);
- QWebEnginePermission::PermissionType permissionType;
-
- if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)
- && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
- permissionType = QWebEnginePermission::PermissionType::MediaAudioCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::MediaVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
- && requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
- else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
-
- Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
-
-#if QT_DEPRECATED_SINCE(6, 8)
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_DEPRECATED
- QWebEnginePage::Feature deprecatedFeature;
-
- if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)
- && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- deprecatedFeature = QWebEnginePage::MediaAudioVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
- deprecatedFeature = QWebEnginePage::MediaAudioCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- deprecatedFeature = QWebEnginePage::MediaVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
- && requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- deprecatedFeature = QWebEnginePage::DesktopAudioVideoCapture;
- else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- deprecatedFeature = QWebEnginePage::DesktopVideoCapture;
-
- Q_EMIT q->featurePermissionRequested(securityOrigin, deprecatedFeature);
- QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 8)
-}
-
#if QT_DEPRECATED_SINCE(6, 8)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
@@ -668,33 +625,19 @@ static QWebEnginePage::Feature toDeprecatedFeature(QWebEnginePermission::Permiss
QT_WARNING_POP
#endif // QT_DEPRECATED_SINCE(6, 8)
-void QWebEnginePagePrivate::runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin)
+void QWebEnginePagePrivate::runFeaturePermissionRequest(
+ QWebEnginePermission::PermissionType permissionType,
+ const QUrl &securityOrigin,
+ int childId, const std::string &serializedToken)
{
Q_Q(QWebEnginePage);
- if (QWebEnginePermission::isPersistent(permissionType)) {
- Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
-#if QT_DEPRECATED_SINCE(6, 8)
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_DEPRECATED
- Q_EMIT q->featurePermissionRequested(securityOrigin, toDeprecatedFeature(permissionType));
- QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 8)
- return;
- }
-
- Q_UNREACHABLE();
-}
-
-void QWebEnginePagePrivate::runMouseLockPermissionRequest(const QUrl &securityOrigin)
-{
- Q_Q(QWebEnginePage);
- Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, QWebEnginePermission::PermissionType::MouseLock));
-
+ Q_EMIT q->permissionRequested(QWebEnginePermission(
+ new QWebEnginePermissionPrivate(securityOrigin, permissionType, profileAdapter(), childId, serializedToken)));
#if QT_DEPRECATED_SINCE(6, 8)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
- Q_EMIT q->featurePermissionRequested(securityOrigin, QWebEnginePage::MouseLock);
+ Q_EMIT q->featurePermissionRequested(securityOrigin, toDeprecatedFeature(permissionType));
QT_WARNING_POP
#endif // QT_DEPRECATED_SINCE(6, 8)
}
@@ -885,12 +828,6 @@ void QWebEnginePagePrivate::showWebAuthDialog(QWebEngineWebAuthUxRequest *reques
Q_EMIT q->webAuthUxRequested(request);
}
-QWebEnginePermission QWebEnginePagePrivate::createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType feature)
-{
- auto *returnPrivate = new QWebEnginePermissionPrivate(securityOrigin, feature, adapter, profileAdapter());
- return QWebEnginePermission(returnPrivate);
-}
-
QWebEnginePage::QWebEnginePage(QObject* parent)
: QObject(parent)
, d_ptr(new QWebEnginePagePrivate())
@@ -1918,7 +1855,7 @@ void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEngine
Q_UNREACHABLE();
}
- d->adapter->setPermission(securityOrigin, f, s);
+ d->adapter->setPermission(securityOrigin, f, s, {});
}
QT_WARNING_POP
#endif // QT_DEPRECATED_SINCE(6, 8)
diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h
index 76b4a4d9d..ba1fbc6d5 100644
--- a/src/core/api/qwebenginepage_p.h
+++ b/src/core/api/qwebenginepage_p.h
@@ -148,9 +148,8 @@ public:
void authenticationRequired(
QSharedPointer<QtWebEngineCore::AuthenticationDialogController>) override;
void releaseProfile() override;
- void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) override;
- void runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin) override;
- void runMouseLockPermissionRequest(const QUrl &securityOrigin) override;
+ void runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin,
+ int childId, const std::string &serializedToken) override;
void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) override;
void runFileSystemAccessRequest(QWebEngineFileSystemAccessRequest) override;
QObject *accessibilityParentObject() override;
@@ -181,7 +180,6 @@ public:
const QRect &bounds, bool autoselectFirstSuggestion) override;
void hideAutofillPopup() override;
void showWebAuthDialog(QWebEngineWebAuthUxRequest *controller) override;
- QWebEnginePermission createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) override;
QtWebEngineCore::ProfileAdapter *profileAdapter() override;
QtWebEngineCore::WebContentsAdapter *webContentsAdapter() override;
diff --git a/src/core/api/qwebenginepermission.cpp b/src/core/api/qwebenginepermission.cpp
index ec62f0e4c..1d1b12b7e 100644
--- a/src/core/api/qwebenginepermission.cpp
+++ b/src/core/api/qwebenginepermission.cpp
@@ -20,11 +20,12 @@ QWebEnginePermissionPrivate::QWebEnginePermissionPrivate()
/*! \internal */
QWebEnginePermissionPrivate::QWebEnginePermissionPrivate(const QUrl &origin_, QWebEnginePermission::PermissionType permissionType_,
- QSharedPointer<QtWebEngineCore::WebContentsAdapter> webContentsAdapter_, QtWebEngineCore::ProfileAdapter *profileAdapter_)
+ QtWebEngineCore::ProfileAdapter *profileAdapter_, int childId_, const std::string &serializedToken_)
: QSharedData()
, origin(origin_)
, permissionType(permissionType_)
- , webContentsAdapter(webContentsAdapter_)
+ , childId(childId_)
+ , serializedToken(serializedToken_)
, profileAdapter(profileAdapter_)
{
}
@@ -114,15 +115,12 @@ bool QWebEnginePermission::equals(const QWebEnginePermission &other) const
return false;
if (!isPersistent(d_ptr->permissionType)) {
- if (d_ptr->webContentsAdapter != other.d_ptr->webContentsAdapter)
+ if (d_ptr->childId != other.d_ptr->childId
+ && d_ptr->serializedToken != other.d_ptr->serializedToken)
return false;
} else {
- QtWebEngineCore::ProfileAdapter *thisProfile = d_ptr->webContentsAdapter
- ? d_ptr->webContentsAdapter.toStrongRef()->profileAdapter()
- : d_ptr->profileAdapter.get();
- QtWebEngineCore::ProfileAdapter *otherProfile = d_ptr->webContentsAdapter
- ? other.d_ptr->webContentsAdapter.toStrongRef()->profileAdapter()
- : other.d_ptr->profileAdapter.get();
+ QtWebEngineCore::ProfileAdapter *thisProfile = d_ptr->profileAdapter.get();
+ QtWebEngineCore::ProfileAdapter *otherProfile = other.d_ptr->profileAdapter.get();
if (thisProfile != otherProfile)
return false;
@@ -201,11 +199,7 @@ QWebEnginePermission::State QWebEnginePermission::state() const
{
if (!isValid())
return State::Invalid;
- if (d_ptr->webContentsAdapter)
- return d_ptr->webContentsAdapter.toStrongRef()->getPermissionState(origin(), permissionType());
- if (d_ptr->profileAdapter)
- return d_ptr->profileAdapter->getPermissionState(origin(), permissionType());
- Q_UNREACHABLE_RETURN(State::Ask);
+ return d_ptr->profileAdapter->getPermissionState(origin(), permissionType(), d_ptr->childId, d_ptr->serializedToken);
}
/*!
@@ -227,7 +221,7 @@ bool QWebEnginePermission::isValid() const
return false;
if (permissionType() == PermissionType::Unsupported)
return false;
- if (!d_ptr->profileAdapter && !d_ptr->webContentsAdapter)
+ if (!d_ptr->profileAdapter)
return false;
if (!d_ptr->origin.isValid())
return false;
@@ -243,10 +237,7 @@ void QWebEnginePermission::grant() const
{
if (!isValid())
return;
- if (d_ptr->webContentsAdapter)
- d_ptr->webContentsAdapter.toStrongRef()->setPermission(origin(), permissionType(), State::Granted);
- else if (d_ptr->profileAdapter)
- d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Granted);
+ d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Granted, d_ptr->childId, d_ptr->serializedToken);
}
/*!
@@ -258,10 +249,7 @@ void QWebEnginePermission::deny() const
{
if (!isValid())
return;
- if (d_ptr->webContentsAdapter)
- d_ptr->webContentsAdapter.toStrongRef()->setPermission(origin(), permissionType(), State::Denied);
- else if (d_ptr->profileAdapter)
- d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Denied);
+ d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Denied, d_ptr->childId, d_ptr->serializedToken);
}
/*!
@@ -279,10 +267,7 @@ void QWebEnginePermission::reset() const
{
if (!isValid())
return;
- if (d_ptr->webContentsAdapter)
- d_ptr->webContentsAdapter.toStrongRef()->setPermission(origin(), permissionType(), State::Ask);
- else if (d_ptr->profileAdapter)
- d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Ask);
+ d_ptr->profileAdapter->setPermission(origin(), permissionType(), State::Ask, d_ptr->childId, d_ptr->serializedToken);
}
/*!
diff --git a/src/core/api/qwebenginepermission_p.h b/src/core/api/qwebenginepermission_p.h
index c6b525b31..aabb5c4b9 100644
--- a/src/core/api/qwebenginepermission_p.h
+++ b/src/core/api/qwebenginepermission_p.h
@@ -33,12 +33,14 @@ struct QWebEnginePermissionPrivate : public QSharedData
{
Q_WEBENGINECORE_EXPORT QWebEnginePermissionPrivate();
Q_WEBENGINECORE_EXPORT QWebEnginePermissionPrivate(const QUrl &, QWebEnginePermission::PermissionType,
- QSharedPointer<QtWebEngineCore::WebContentsAdapter>, QtWebEngineCore::ProfileAdapter *);
+ QtWebEngineCore::ProfileAdapter *, int = -1, const std::string & = std::string());
QUrl origin;
QWebEnginePermission::PermissionType permissionType;
- QWeakPointer<QtWebEngineCore::WebContentsAdapter> webContentsAdapter;
+ int childId = -1;
+ std::string serializedToken;
+
QPointer<QtWebEngineCore::ProfileAdapter> profileAdapter;
};
diff --git a/src/core/api/qwebengineprofile.cpp b/src/core/api/qwebengineprofile.cpp
index b6c308cf6..f7f6ab551 100644
--- a/src/core/api/qwebengineprofile.cpp
+++ b/src/core/api/qwebengineprofile.cpp
@@ -1018,7 +1018,7 @@ QWebEnginePermission QWebEngineProfile::queryPermission(const QUrl &securityOrig
return QWebEnginePermission(new QWebEnginePermissionPrivate());
}
- auto *pvt = new QWebEnginePermissionPrivate(securityOrigin, permissionType, nullptr, d->profileAdapter());
+ auto *pvt = new QWebEnginePermissionPrivate(securityOrigin, permissionType, d->profileAdapter());
return QWebEnginePermission(pvt);
}
diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp
index 848a92986..d691c65ea 100644
--- a/src/core/media_capture_devices_dispatcher.cpp
+++ b/src/core/media_capture_devices_dispatcher.cpp
@@ -11,6 +11,8 @@
#include "web_contents_delegate_qt.h"
#include "web_contents_view_qt.h"
#include "web_engine_settings.h"
+#include "permission_manager_qt.h"
+#include "type_conversion.h"
#include "base/strings/strcat.h"
#include "blink/public/common/page/page_zoom.h"
@@ -21,6 +23,8 @@
#include "content/public/browser/desktop_streams_registry.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/media_capture_devices.h"
+#include "content/public/browser/permission_controller_delegate.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_manager_base.h"
@@ -493,8 +497,17 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(
}
enqueueMediaAccessRequest(webContents, request, std::move(callback), id);
- // We might not require this approval for pepper requests.
- adapterClient->runMediaAccessPermissionRequest(toQt(request.security_origin), flags);
+
+ PermissionManagerQt *permissionManager = static_cast<PermissionManagerQt *>(
+ webContents->GetBrowserContext()->GetPermissionControllerDelegate());
+ permissionManager->requestMediaPermissions(
+ content::RenderFrameHost::FromID(request.render_process_id, request.render_frame_id),
+ flags,
+ base::BindOnce(
+ &MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse,
+ base::Unretained(this),
+ webContents,
+ toQt(request.url_origin)));
}
void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content::WebContents *webContents, const content::MediaStreamRequest &request, content::MediaResponseCallback callback)
@@ -558,9 +571,18 @@ void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(content::WebConte
RequestsQueue &queue(it->second);
content::MediaStreamRequest &request = queue.front()->request;
+ WebContentsAdapterClient::MediaRequestFlags flags = mediaRequestFlagsForRequest(request);
- WebContentsAdapterClient *adapterClient = WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(webContents)->GetView())->client();
- adapterClient->runMediaAccessPermissionRequest(toQt(request.security_origin), mediaRequestFlagsForRequest(request));
+ PermissionManagerQt *permissionManager = static_cast<PermissionManagerQt *>(
+ webContents->GetBrowserContext()->GetPermissionControllerDelegate());
+ permissionManager->requestMediaPermissions(
+ content::RenderFrameHost::FromID(request.render_process_id, request.render_frame_id),
+ flags,
+ base::BindOnce(
+ &MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse,
+ base::Unretained(this),
+ webContents,
+ toQt(request.url_origin)));
}
void MediaCaptureDevicesDispatcher::getDefaultDevices(const std::string &audioDeviceId, const std::string &videoDeviceId,
diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp
index 5258f15cf..eae98b350 100644
--- a/src/core/permission_manager_qt.cpp
+++ b/src/core/permission_manager_qt.cpp
@@ -40,7 +40,7 @@ static QWebEnginePermission::PermissionType toQt(blink::PermissionType type)
case blink::PermissionType::VIDEO_CAPTURE:
return QWebEnginePermission::PermissionType::MediaVideoCapture;
case blink::PermissionType::DISPLAY_CAPTURE:
- return QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
+ return QWebEnginePermission::PermissionType::DesktopVideoCapture;
// We treat these both as read/write since we do not currently have a
// ClipboardSanitizedWrite permission type.
case blink::PermissionType::CLIPBOARD_READ_WRITE:
@@ -56,6 +56,8 @@ static QWebEnginePermission::PermissionType toQt(blink::PermissionType type)
case blink::PermissionType::WINDOW_MANAGEMENT:
case blink::PermissionType::BACKGROUND_SYNC:
case blink::PermissionType::NUM:
+ case blink::PermissionType::TOP_LEVEL_STORAGE_ACCESS:
+ case blink::PermissionType::SPEAKER_SELECTION:
return QWebEnginePermission::PermissionType::Unsupported;
case blink::PermissionType::MIDI_SYSEX:
case blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
@@ -72,16 +74,13 @@ static QWebEnginePermission::PermissionType toQt(blink::PermissionType type)
case blink::PermissionType::AR:
case blink::PermissionType::VR:
case blink::PermissionType::STORAGE_ACCESS_GRANT:
- case blink::PermissionType::TOP_LEVEL_STORAGE_ACCESS:
case blink::PermissionType::CAPTURED_SURFACE_CONTROL:
case blink::PermissionType::SMART_CARD:
case blink::PermissionType::WEB_PRINTING:
- case blink::PermissionType::SPEAKER_SELECTION:
case blink::PermissionType::KEYBOARD_LOCK:
case blink::PermissionType::AUTOMATIC_FULLSCREEN:
case blink::PermissionType::HAND_TRACKING:
case blink::PermissionType::WEB_APP_INSTALLATION:
- LOG(INFO) << "Unexpected unsupported Blink permission type: " << static_cast<int>(type);
break;
}
return QWebEnginePermission::PermissionType::Unsupported;
@@ -107,16 +106,50 @@ static blink::PermissionType toBlink(QWebEnginePermission::PermissionType permis
return blink::PermissionType::LOCAL_FONTS;
case QWebEnginePermission::PermissionType::MouseLock:
return blink::PermissionType::POINTER_LOCK;
- case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
- LOG(INFO) << "Unexpected unsupported WebEngine permission type: " << static_cast<int>(permissionType);
- Q_FALLTHROUGH();
case QWebEnginePermission::PermissionType::Unsupported:
return blink::PermissionType::NUM;
+ case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+ break;
}
Q_UNREACHABLE_RETURN(blink::PermissionType::NUM);
}
+static std::vector<QWebEnginePermission::PermissionType> toQt(
+ const std::vector<blink::PermissionType> &blinkPermissions)
+{
+ // This function handles the edge case differences between our permission types and Blink's;
+ // namely, MediaAudioVideoCapture and DesktopAudioVideoCapture
+ std::vector<QWebEnginePermission::PermissionType> permissions;
+ for (auto &p : blinkPermissions) {
+ permissions.push_back(toQt(p));
+ }
+
+ for (auto i1 = permissions.begin(); i1 != permissions.end(); ++i1) {
+ if (*i1 == QWebEnginePermission::PermissionType::MediaAudioCapture) {
+ for (auto i2 = permissions.begin(); i2 != permissions.end(); ++i2) {
+ if (*i2 == QWebEnginePermission::PermissionType::MediaVideoCapture) {
+ // Merge MediaAudioCapture and MediaVideoCapture into MediaAudioVideoCapture
+ *i1 = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
+ permissions.erase(i2);
+ break;
+ }
+ }
+ } else if (*i1 == QWebEnginePermission::PermissionType::DesktopVideoCapture) {
+ for (auto i2 = i1 + 1; i2 != permissions.end(); ++i2) {
+ if (*i2 == QWebEnginePermission::PermissionType::DesktopVideoCapture) {
+ // Double DesktopVideoCapture means we actually need DesktopAudioVideoCapture
+ *i2 = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
+ i1 = permissions.erase(i1);
+ break;
+ }
+ }
+ }
+ }
+
+ return permissions;
+}
+
static QWebEnginePermission::State toQt(blink::mojom::PermissionStatus state)
{
switch (state) {
@@ -154,6 +187,8 @@ std::string permissionTypeString(QWebEnginePermission::PermissionType permission
return "MediaVideoCapture";
case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
return "DesktopAudioVideoCapture";
+ case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+ return "DesktopVideoCapture";
case QWebEnginePermission::PermissionType::MouseLock:
return "MouseLock";
case QWebEnginePermission::PermissionType::Notifications:
@@ -213,6 +248,8 @@ PermissionManagerQt::PermissionManagerQt(ProfileAdapter *profileAdapter)
m_permissionTypes.push_back(QWebEnginePermission::PermissionType::MediaAudioCapture);
m_permissionTypes.push_back(QWebEnginePermission::PermissionType::MediaVideoCapture);
+ m_permissionTypes.push_back(QWebEnginePermission::PermissionType::DesktopAudioVideoCapture);
+ m_permissionTypes.push_back(QWebEnginePermission::PermissionType::DesktopVideoCapture);
m_permissionTypes.push_back(QWebEnginePermission::PermissionType::MouseLock);
m_permissionTypes.push_back(QWebEnginePermission::PermissionType::Notifications);
m_permissionTypes.push_back(QWebEnginePermission::PermissionType::Geolocation);
@@ -239,38 +276,83 @@ PermissionManagerQt::~PermissionManagerQt()
commit();
}
+// static
+content::GlobalRenderFrameHostToken PermissionManagerQt::deserializeToken(int childId, const std::string &serializedToken)
+{
+ auto maybeToken = base::UnguessableToken::DeserializeFromString(serializedToken);
+ if (maybeToken)
+ return content::GlobalRenderFrameHostToken(childId, blink::LocalFrameToken(maybeToken.value()));
+
+ return content::GlobalRenderFrameHostToken();
+}
+
void PermissionManagerQt::setPermission(
const QUrl &url,
- QWebEnginePermission::PermissionType permissionType,
- QWebEnginePermission::State state,
- content::RenderFrameHost *rfh)
+ const QWebEnginePermission::PermissionType permissionType,
+ const QWebEnginePermission::State state,
+ const content::GlobalRenderFrameHostToken &token)
+{
+ if (permissionType == QWebEnginePermission::PermissionType::MediaAudioVideoCapture) {
+ setPermissionImpl(url, QWebEnginePermission::PermissionType::MediaAudioCapture, state, token);
+ setPermissionImpl(url, QWebEnginePermission::PermissionType::MediaVideoCapture, state, token);
+ return;
+ }
+
+ setPermissionImpl(url, permissionType, state, token);
+}
+
+void PermissionManagerQt::setPermission(
+ const QUrl &url,
+ const QWebEnginePermission::PermissionType permissionType,
+ const QWebEnginePermission::State state,
+ int childId, const std::string &serializedToken)
+{
+ content::GlobalRenderFrameHostToken token;
+ auto maybeToken = base::UnguessableToken::DeserializeFromString(serializedToken);
+ if (maybeToken)
+ token = content::GlobalRenderFrameHostToken(childId, blink::LocalFrameToken(maybeToken.value()));
+
+ setPermission(url, permissionType, state, token);
+}
+
+void PermissionManagerQt::setPermissionImpl(
+ const QUrl &url,
+ const QWebEnginePermission::PermissionType permissionTypeQt,
+ const QWebEnginePermission::State permissionStateQt,
+ const content::GlobalRenderFrameHostToken &frameToken)
{
+ const blink::PermissionType permissionTypeBlink = toBlink(permissionTypeQt);
+ const blink::mojom::PermissionStatus permissionStateBlink = toBlink(permissionStateQt);
+
// Normalize the QUrl to Chromium origin form.
const GURL gorigin = toGurl(url).DeprecatedGetOriginAsURL();
const QUrl origin = gorigin.is_empty() ? url : toQt(gorigin);
if (origin.isEmpty())
return;
- // Send eligible permissions with an associated rfh to the transient store. When pre-granting
+ // Send eligible permissions with an associated frameToken to the transient store. When pre-granting
// a non-persistent permission (or pre-granting any permission in AskEveryTime mode), it is allowed
// to pass through the persistent store. It will be moved to the transient store and associated
- // with a rfh the next time its status is requested.
- bool inTransientStore = rfh && (!QWebEnginePermission::isPersistent(permissionType) || !m_persistence);
+ // with a frameToken the next time its status is requested.
+ bool inTransientStore = frameToken.child_id != content::kInvalidChildProcessUniqueId
+ && (!QWebEnginePermission::isPersistent(permissionTypeQt) || !m_persistence);
- blink::mojom::PermissionStatus blinkStatus = toBlink(state);
- if (state == QWebEnginePermission::State::Ask) {
+ blink::mojom::PermissionStatus blinkStatus = permissionStateBlink;
+ if (permissionStateQt == QWebEnginePermission::State::Ask) {
if (inTransientStore)
- resetTransientPermission(toBlink(permissionType), gorigin, rfh->GetGlobalFrameToken());
+ resetTransientPermission(permissionTypeBlink, gorigin, frameToken);
else
- ResetPermission(toBlink(permissionType), gorigin, gorigin);
+ ResetPermission(permissionTypeBlink, gorigin, gorigin);
} else {
if (inTransientStore)
- setTransientPermission(toBlink(permissionType), gorigin, state == QWebEnginePermission::State::Granted, rfh->GetGlobalFrameToken());
+ setTransientPermission(permissionTypeBlink, gorigin,
+ permissionStateQt == QWebEnginePermission::State::Granted, frameToken);
else
- setPersistentPermission(toBlink(permissionType), gorigin, state == QWebEnginePermission::State::Granted);
+ setPersistentPermission(permissionTypeBlink,
+ gorigin, permissionStateQt == QWebEnginePermission::State::Granted);
auto it = m_requests.begin();
while (it != m_requests.end()) {
- if (it->origin == origin && it->type == permissionType) {
+ if (it->origin == origin && it->type == permissionTypeQt) {
std::move(it->callback).Run(blinkStatus);
it = m_requests.erase(it);
} else
@@ -292,10 +374,10 @@ void PermissionManagerQt::setPermission(
if (subscription->embedding_origin != gorigin)
continue;
- if (subscription->permission != toBlink(permissionType))
+ if (subscription->permission != permissionTypeBlink)
continue;
- if ((!QWebEnginePermission::isPersistent(permissionType) || !m_persistence)
- && targetRfh && targetRfh != rfh)
+ if ((!QWebEnginePermission::isPersistent(permissionTypeQt) || !m_persistence)
+ && targetRfh && targetRfh != content::RenderFrameHost::FromFrameToken(frameToken))
continue;
// Behavior in callbacks may differ depending on the denial reason. Until we have
@@ -312,7 +394,7 @@ void PermissionManagerQt::setPermission(
std::move(callback).Run();
}
- if (state == QWebEnginePermission::State::Ask)
+ if (permissionStateQt == QWebEnginePermission::State::Ask)
return;
auto it = m_multiRequests.begin();
@@ -321,32 +403,37 @@ void PermissionManagerQt::setPermission(
bool answerable = true;
std::vector<blink::mojom::PermissionStatus> result;
result.reserve(it->types.size());
- for (blink::PermissionType permission : it->types) {
- if (toQt(permission) == QWebEnginePermission::PermissionType::Unsupported) {
+ for (blink::PermissionType currentPermissionType : it->types) {
+ if (toQt(currentPermissionType) == QWebEnginePermission::PermissionType::Unsupported) {
result.push_back(blink::mojom::PermissionStatus::DENIED);
continue;
}
blink::mojom::PermissionStatus permissionStatus;
if (inTransientStore)
- permissionStatus = toBlink(getPermissionState(url, permissionType, rfh));
+ permissionStatus = toBlink(getPermissionState(url, toQt(currentPermissionType), frameToken));
else
- permissionStatus = GetPermissionStatus(permission, gorigin, GURL());
+ permissionStatus = GetPermissionStatus(currentPermissionType, gorigin, GURL());
- if (permissionStatus == toBlink(state)) {
+ if (permissionStatus == permissionStateBlink) {
if (permissionStatus == blink::mojom::PermissionStatus::ASK) {
answerable = false;
break;
}
result.push_back(permissionStatus);
- } else {
+ } else if (!m_persistence) {
// Reached when the PersistentPermissionsPolicy is set to AskEveryTime
- result.push_back(toBlink(state));
+ result.push_back(permissionStateBlink);
+ } else {
+ // Not all of the permissions in this request have been set yet, bail and wait for the next setPermission() call
+ answerable = false;
+ break;
}
}
if (answerable) {
- std::move(it->callback).Run(result);
+ if (!it->callback.is_null())
+ std::move(it->callback).Run(result);
it = m_multiRequests.erase(it);
continue;
}
@@ -355,23 +442,44 @@ void PermissionManagerQt::setPermission(
}
}
-QWebEnginePermission::State PermissionManagerQt::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
- content::RenderFrameHost *rfh)
+QWebEnginePermission::State PermissionManagerQt::getPermissionState(
+ const QUrl &origin,
+ const QWebEnginePermission::PermissionType permissionType,
+ const content::GlobalRenderFrameHostToken &frameToken)
{
- if (rfh) {
- // Ignore the origin parameter
- return toQt(GetPermissionStatusForCurrentDocument(toBlink(permissionType), rfh, false));
+ std::vector<QWebEnginePermission::PermissionType> types;
+ if (permissionType == QWebEnginePermission::PermissionType::MediaAudioVideoCapture) {
+ types.push_back(QWebEnginePermission::PermissionType::MediaAudioCapture);
+ types.push_back(QWebEnginePermission::PermissionType::MediaVideoCapture);
+ } else {
+ types.push_back(permissionType);
+ }
+
+ auto *rfh = content::RenderFrameHost::FromFrameToken(frameToken);
+ QWebEnginePermission::State returnState = QWebEnginePermission::State::Invalid;
+ for (auto type : types) {
+ QWebEnginePermission::State state = rfh
+ ? toQt(GetPermissionStatusForCurrentDocument(toBlink(type), rfh, false))
+ : toQt(GetPermissionStatus(toBlink(type), toGurl(origin), GURL()));
+
+ if (returnState == QWebEnginePermission::State::Invalid)
+ returnState = state;
+ else if (returnState != state)
+ returnState = QWebEnginePermission::State::Ask;
}
- return toQt(GetPermissionStatus(toBlink(permissionType), toGurl(origin), GURL()));
+ return returnState;
}
-QList<QWebEnginePermission> PermissionManagerQt::listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType)
+QList<QWebEnginePermission> PermissionManagerQt::listPermissions(
+ const QUrl &origin,
+ const QWebEnginePermission::PermissionType permissionType)
{
Q_ASSERT(origin.isEmpty() || permissionType == QWebEnginePermission::PermissionType::Unsupported);
+
QList<QWebEnginePermission> returnList;
- GURL gorigin = toGurl(origin).DeprecatedGetOriginAsURL();
- std::string originSpec = gorigin.spec();
+ const GURL gorigin = toGurl(origin).DeprecatedGetOriginAsURL();
+ const std::string originSpec = gorigin.spec();
if (!origin.isEmpty() && !gorigin.is_valid())
return returnList;
@@ -382,7 +490,7 @@ QList<QWebEnginePermission> PermissionManagerQt::listPermissions(const QUrl &ori
else
types.push_back(permissionType);
- for (auto &type : types) {
+ for (const auto &type : types) {
// Transient types may end up in the permission store as an implementation detail,
// but we do not want to expose them to callers.
if (!QWebEnginePermission::isPersistent(type))
@@ -399,7 +507,8 @@ QList<QWebEnginePermission> PermissionManagerQt::listPermissions(const QUrl &ori
if (!originSpec.empty() && entry.first != originSpec)
continue;
- auto *pvt = new QWebEnginePermissionPrivate(toQt(GURL(std::string_view(entry.first))), type, nullptr, m_profileAdapter.get());
+ auto *pvt = new QWebEnginePermissionPrivate(
+ toQt(GURL(std::string_view(entry.first))), type, m_profileAdapter.get());
returnList.push_back(QWebEnginePermission(pvt));
}
}
@@ -407,6 +516,78 @@ QList<QWebEnginePermission> PermissionManagerQt::listPermissions(const QUrl &ori
return returnList;
}
+void PermissionManagerQt::requestMediaPermissions(
+ content::RenderFrameHost *render_frame_host,
+ const WebContentsAdapterClient::MediaRequestFlags flags,
+ base::OnceCallback<void(WebContentsAdapterClient::MediaRequestFlags authorizationFlags)> callback)
+{
+ std::vector<blink::PermissionType> permissionTypesBlink;
+ if (flags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
+ permissionTypesBlink.push_back(blink::PermissionType::AUDIO_CAPTURE);
+ if (flags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
+ permissionTypesBlink.push_back(blink::PermissionType::VIDEO_CAPTURE);
+ if (flags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
+ || flags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture)) {
+ permissionTypesBlink.push_back(blink::PermissionType::DISPLAY_CAPTURE);
+ if (flags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)) {
+ // Inject a second copy of the permission type into the request,
+ // so we can distinguish between DesktopVideoCapture and DesktopAudioVideoCapture.
+ permissionTypesBlink.push_back(blink::PermissionType::DISPLAY_CAPTURE);
+ }
+ }
+
+ content::PermissionRequestDescription description(permissionTypesBlink, false, render_frame_host->GetLastCommittedOrigin().GetURL());
+
+ RequestPermissions(render_frame_host, description, base::BindOnce([](
+ std::vector<blink::PermissionType> permissionTypesBlink,
+ base::OnceCallback<void(WebContentsAdapterClient::MediaRequestFlags authorizationFlags)> callback,
+ const std::vector<blink::mojom::PermissionStatus> &statuses)
+ {
+ // This callback converts the Blink permission types to MediaRequestFlags,
+ // and then runs the callback initially passed to requestMediaPermissions().
+ DCHECK(permissionTypesBlink.size() == statuses.size());
+ WebContentsAdapterClient::MediaRequestFlags flags = WebContentsAdapterClient::MediaRequestFlag::MediaNone;
+ for (uint i = 0; i < statuses.size(); ++i) {
+ if (statuses[i] == blink::mojom::PermissionStatus::GRANTED) {
+ switch (permissionTypesBlink[i]) {
+ case blink::PermissionType::AUDIO_CAPTURE:
+ flags.setFlag(WebContentsAdapterClient::MediaRequestFlag::MediaAudioCapture);
+ break;
+ case blink::PermissionType::VIDEO_CAPTURE:
+ flags.setFlag(WebContentsAdapterClient::MediaRequestFlag::MediaVideoCapture);
+ break;
+ case blink::PermissionType::DISPLAY_CAPTURE:
+ flags.setFlag(WebContentsAdapterClient::MediaRequestFlag::MediaDesktopAudioCapture);
+ flags.setFlag(WebContentsAdapterClient::MediaRequestFlag::MediaDesktopVideoCapture);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ }
+ std::move(callback).Run(flags);
+ }, permissionTypesBlink, std::move(callback)));
+}
+
+// Needed for the rare cases where a RenderFrameHost remains the same even after
+// a cross-origin navigation (e.g. inside an iframe). Needs to be called every
+// time transient permissions are accessed.
+void PermissionManagerQt::onCrossOriginNavigation(content::RenderFrameHost *render_frame_host)
+{
+ if (!render_frame_host)
+ return;
+
+ auto frameToken = render_frame_host->GetGlobalFrameToken();
+ auto &permissionsForToken = m_transientPermissions[frameToken];
+ if (!permissionsForToken.size())
+ return;
+
+ GURL savedOrigin = get<0>(permissionsForToken[0]);
+ if (render_frame_host->GetLastCommittedOrigin().GetURL() != savedOrigin)
+ m_transientPermissions.erase(frameToken);
+}
+
void PermissionManagerQt::commit()
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -414,15 +595,18 @@ void PermissionManagerQt::commit()
m_prefService->CommitPendingWrite();
}
-void PermissionManagerQt::RequestPermissions(content::RenderFrameHost *frameHost,
- const content::PermissionRequestDescription &requestDescription,
- base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
+void PermissionManagerQt::RequestPermissions(
+ content::RenderFrameHost *frameHost,
+ const content::PermissionRequestDescription &requestDescription,
+ base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
{
if (requestDescription.requesting_origin.is_empty()) {
- std::move(callback).Run(std::vector<content::PermissionStatus>(requestDescription.permissions.size(), blink::mojom::PermissionStatus::DENIED));
+ std::move(callback).Run(std::vector<content::PermissionStatus>(requestDescription.permissions.size(),
+ blink::mojom::PermissionStatus::DENIED));
return;
}
+ const auto frameToken = frameHost->GetGlobalFrameToken();
WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>(
content::WebContents::FromRenderFrameHost(frameHost)->GetDelegate());
Q_ASSERT(contentsDelegate);
@@ -430,53 +614,54 @@ void PermissionManagerQt::RequestPermissions(content::RenderFrameHost *frameHost
bool answerable = true;
std::vector<content::PermissionStatus> result;
result.reserve(requestDescription.permissions.size());
- for (blink::PermissionType permission : requestDescription.permissions) {
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (permissionType == QWebEnginePermission::PermissionType::Unsupported) {
+ for (const blink::PermissionType permissionTypeBlink : requestDescription.permissions) {
+ const QWebEnginePermission::PermissionType permissionTypeQt = toQt(permissionTypeBlink);
+ if (permissionTypeQt == QWebEnginePermission::PermissionType::Unsupported) {
result.push_back(blink::mojom::PermissionStatus::DENIED);
continue;
}
- blink::mojom::PermissionStatus permissionStatus = getStatusFromSettings(permission, contentsDelegate->webEngineSettings());
- if (permissionStatus == blink::mojom::PermissionStatus::ASK) {
+ blink::mojom::PermissionStatus permissionStatusBlink = getStatusFromSettings(
+ permissionTypeBlink, contentsDelegate->webEngineSettings());
+ if (permissionStatusBlink == blink::mojom::PermissionStatus::ASK) {
const GURL &rorigin = requestDescription.requesting_origin;
+ bool maybePreGranted = false;
if (!m_persistence) {
- answerable = false;
- break;
+ maybePreGranted = true;
}
- bool inTransientStore = !QWebEnginePermission::isPersistent(toQt(permission));
+ bool inTransientStore = !QWebEnginePermission::isPersistent(permissionTypeQt) || maybePreGranted;
if (inTransientStore) {
- permissionStatus = getTransientPermissionStatus(permission, rorigin, frameHost->GetGlobalFrameToken());
+ permissionStatusBlink = getTransientPermissionStatus(permissionTypeBlink, rorigin, frameToken);
- if (permissionStatus != blink::mojom::PermissionStatus::ASK) {
- result.push_back(permissionStatus);
+ if (permissionStatusBlink != blink::mojom::PermissionStatus::ASK) {
+ result.push_back(permissionStatusBlink);
continue;
}
// Fall through to check if permission was pre-granted (and thus landed in the permanent store)
}
- permissionStatus = GetPermissionStatus(permission, rorigin, rorigin);
+ permissionStatusBlink = GetPermissionStatus(permissionTypeBlink, rorigin, rorigin);
- if (inTransientStore && permissionStatus != blink::mojom::PermissionStatus::ASK) {
- // Move the pre-granted permission to the transient store and associate it with the rfh
- ResetPermission(permission, rorigin, rorigin);
- setTransientPermission(permission, rorigin, permissionStatus == blink::mojom::PermissionStatus::GRANTED,
- frameHost->GetGlobalFrameToken());
+ if (inTransientStore && permissionStatusBlink != blink::mojom::PermissionStatus::ASK) {
+ // Move the pre-granted permission to the transient store and associate it with a frame token
+ ResetPermission(permissionTypeBlink, rorigin, rorigin);
+ setTransientPermission(permissionTypeBlink, rorigin,
+ permissionStatusBlink == blink::mojom::PermissionStatus::GRANTED, frameToken);
}
- if (permissionStatus != blink::mojom::PermissionStatus::ASK) {
+ if (permissionStatusBlink != blink::mojom::PermissionStatus::ASK) {
// Automatically grant/deny without prompt if already asked once
- result.push_back(permissionStatus);
+ result.push_back(permissionStatusBlink);
} else {
answerable = false;
break;
}
} else {
// Reached when clipboard settings have been set
- result.push_back(permissionStatus);
+ result.push_back(permissionStatusBlink);
}
}
@@ -486,80 +671,75 @@ void PermissionManagerQt::RequestPermissions(content::RenderFrameHost *frameHost
}
int request_id = ++m_requestIdCount;
- auto requestOrigin = toQt(requestDescription.requesting_origin);
+ const auto requestOrigin = toQt(requestDescription.requesting_origin);
m_multiRequests.push_back({ request_id, requestDescription.permissions, requestOrigin, std::move(callback) });
- for (blink::PermissionType permission : requestDescription.permissions) {
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (QWebEnginePermission::isPersistent(permissionType))
- contentsDelegate->requestFeaturePermission(permissionType, requestOrigin);
+ auto qtPermissions = toQt(requestDescription.permissions);
+ for (const QWebEnginePermission::PermissionType permissionTypeQt : qtPermissions) {
+ contentsDelegate->requestFeaturePermission(permissionTypeQt, requestOrigin, frameToken);
}
}
-void PermissionManagerQt::RequestPermissionsFromCurrentDocument(content::RenderFrameHost *frameHost,
- const content::PermissionRequestDescription &requestDescription,
- base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
+void PermissionManagerQt::RequestPermissionsFromCurrentDocument(
+ content::RenderFrameHost *frameHost,
+ const content::PermissionRequestDescription &requestDescription,
+ base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)> callback)
{
RequestPermissions(frameHost, requestDescription, std::move(callback));
}
blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatus(
- blink::PermissionType permission,
+ blink::PermissionType permissionTypeBlink,
const GURL& requesting_origin,
const GURL& /*embedding_origin*/)
{
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+ const QWebEnginePermission::PermissionType permissionTypeQt = toQt(permissionTypeBlink);
+ if (permissionTypeQt == QWebEnginePermission::PermissionType::Unsupported)
return blink::mojom::PermissionStatus::DENIED;
- permission = toBlink(toQt(permission)); // Filter out merged/unsupported permissions (e.g. clipboard)
- auto *pref = m_prefService->FindPreference(permissionTypeString(toQt(permission)));
+ permissionTypeBlink = toBlink(toQt(permissionTypeBlink)); // Filter out merged/unsupported permissions (e.g. clipboard)
+ auto *pref = m_prefService->FindPreference(permissionTypeString(permissionTypeQt));
if (!pref)
return blink::mojom::PermissionStatus::ASK; // Permission type not in database
- const auto *permissions = pref->GetValue()->GetIfDict();
- Q_ASSERT(permissions);
+ const auto *permissionsDict = pref->GetValue()->GetIfDict();
+ Q_ASSERT(permissionsDict);
- auto requestedPermission = permissions->FindBool(requesting_origin.DeprecatedGetOriginAsURL().spec());
+ const auto requestedPermission = permissionsDict->FindBool(requesting_origin.DeprecatedGetOriginAsURL().spec());
if (!requestedPermission)
return blink::mojom::PermissionStatus::ASK; // Origin is not in the current permission type's database
- // Workaround: local fonts are entirely managed by Chromium, which only calls RequestPermission() _after_
- // it's checked whether the permission has been granted. By always returning ASK, we force the request to
- // come through every time.
- if (permission == blink::PermissionType::LOCAL_FONTS && !m_persistence)
- return blink::mojom::PermissionStatus::ASK;
-
if (requestedPermission.value())
return blink::mojom::PermissionStatus::GRANTED;
return blink::mojom::PermissionStatus::DENIED;
}
blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForCurrentDocument(
- blink::PermissionType permission,
+ blink::PermissionType permissionTypeBlink,
content::RenderFrameHost *render_frame_host, bool)
{
Q_ASSERT(render_frame_host);
- if (permission == blink::PermissionType::CLIPBOARD_READ_WRITE ||
- permission == blink::PermissionType::CLIPBOARD_SANITIZED_WRITE) {
+ if (permissionTypeBlink == blink::PermissionType::CLIPBOARD_READ_WRITE ||
+ permissionTypeBlink == blink::PermissionType::CLIPBOARD_SANITIZED_WRITE) {
WebContentsDelegateQt *delegate = static_cast<WebContentsDelegateQt *>(
content::WebContents::FromRenderFrameHost(render_frame_host)->GetDelegate());
Q_ASSERT(delegate);
- auto status = getStatusFromSettings(permission, delegate->webEngineSettings());
+ auto status = getStatusFromSettings(permissionTypeBlink, delegate->webEngineSettings());
if (status != blink::mojom::PermissionStatus::ASK)
return status;
}
- permission = toBlink(toQt(permission)); // Filter out merged/unsupported permissions (e.g. clipboard)
- if (toQt(permission) == QWebEnginePermission::PermissionType::Unsupported)
+ permissionTypeBlink = toBlink(toQt(permissionTypeBlink)); // Filter out merged/unsupported permissions (e.g. clipboard)
+ QWebEnginePermission::PermissionType permissionTypeQt = toQt(permissionTypeBlink);
+ if (permissionTypeQt == QWebEnginePermission::PermissionType::Unsupported)
return blink::mojom::PermissionStatus::DENIED;
GURL origin = render_frame_host->GetLastCommittedOrigin().GetURL();
auto status = blink::mojom::PermissionStatus::ASK;
- bool inTransientStore = !QWebEnginePermission::isPersistent(toQt(permission)) || !m_persistence;
+ const bool inTransientStore = !QWebEnginePermission::isPersistent(permissionTypeQt) || !m_persistence;
if (inTransientStore) {
- status = getTransientPermissionStatus(permission, origin, render_frame_host->GetGlobalFrameToken());
+ status = getTransientPermissionStatus(permissionTypeBlink, origin, render_frame_host->GetGlobalFrameToken());
if (status != blink::mojom::PermissionStatus::ASK) {
return status;
@@ -568,12 +748,12 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForCurren
// Fall through to check if permission was pre-granted (and thus landed in the permanent store)
}
- status = GetPermissionStatus(permission, origin, origin);
+ status = GetPermissionStatus(permissionTypeBlink, origin, origin);
if (inTransientStore && status != blink::mojom::PermissionStatus::ASK) {
// Move the pre-granted permission to the transient store and associate it with the rfh
- ResetPermission(permission, origin, origin);
- setTransientPermission(permission, origin, status == blink::mojom::PermissionStatus::GRANTED,
+ ResetPermission(permissionTypeBlink, origin, origin);
+ setTransientPermission(permissionTypeBlink, origin, status == blink::mojom::PermissionStatus::GRANTED,
render_frame_host->GetGlobalFrameToken());
}
@@ -610,9 +790,9 @@ content::PermissionResult PermissionManagerQt::GetPermissionResultForOriginWitho
}
void PermissionManagerQt::ResetPermission(
- blink::PermissionType permission,
- const GURL& requesting_origin,
- const GURL& /*embedding_origin*/)
+ blink::PermissionType permission,
+ const GURL& requesting_origin,
+ const GURL& /*embedding_origin*/)
{
const QWebEnginePermission::PermissionType permissionType = toQt(permission);
if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
@@ -622,12 +802,12 @@ void PermissionManagerQt::ResetPermission(
updater.Get().Remove(requesting_origin.spec());
}
-blink::mojom::PermissionStatus PermissionManagerQt::getTransientPermissionStatus(blink::PermissionType permission,
+blink::mojom::PermissionStatus PermissionManagerQt::getTransientPermissionStatus(
+ blink::PermissionType permissionTypeBlink,
const GURL& requesting_origin,
content::GlobalRenderFrameHostToken token)
{
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+ if (toQt(permissionTypeBlink) == QWebEnginePermission::PermissionType::Unsupported)
return blink::mojom::PermissionStatus::DENIED;
if (!m_transientPermissions.contains(token))
@@ -635,8 +815,10 @@ blink::mojom::PermissionStatus PermissionManagerQt::getTransientPermissionStatus
auto &permissionsForToken = m_transientPermissions[token];
for (auto p = permissionsForToken.begin(); p != permissionsForToken.end(); ++p) {
- if (get<0>(*p) == requesting_origin && get<1>(*p) == permission) {
- return get<2>(*p) ? blink::mojom::PermissionStatus::GRANTED : blink::mojom::PermissionStatus::DENIED;
+ if (get<0>(*p) == requesting_origin && get<1>(*p) == permissionTypeBlink) {
+ return get<2>(*p)
+ ? blink::mojom::PermissionStatus::GRANTED
+ : blink::mojom::PermissionStatus::DENIED;
}
}
@@ -644,47 +826,49 @@ blink::mojom::PermissionStatus PermissionManagerQt::getTransientPermissionStatus
}
void PermissionManagerQt::setPersistentPermission(
- blink::PermissionType permission,
- const GURL& requesting_origin,
- bool granted)
+ blink::PermissionType permissionTypeBlink,
+ const GURL& requesting_origin,
+ bool granted)
{
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+ const QWebEnginePermission::PermissionType permissionTypeQt = toQt(permissionTypeBlink);
+ if (permissionTypeQt == QWebEnginePermission::PermissionType::Unsupported)
return;
- if (!m_prefService->FindPreference(permissionTypeString(permissionType)))
+ if (!m_prefService->FindPreference(permissionTypeString(permissionTypeQt)))
return;
- ScopedDictPrefUpdate updater(m_prefService.get(), permissionTypeString(permissionType));
+ ScopedDictPrefUpdate updater(m_prefService.get(), permissionTypeString(permissionTypeQt));
updater.Get().Set(requesting_origin.spec(), granted);
m_prefService->SchedulePendingLossyWrites();
}
-void PermissionManagerQt::setTransientPermission(blink::PermissionType permission,
+void PermissionManagerQt::setTransientPermission(
+ blink::PermissionType permissionTypeBlink,
const GURL& requesting_origin,
bool granted,
content::GlobalRenderFrameHostToken token)
{
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+ const QWebEnginePermission::PermissionType permissionTypeQt = toQt(permissionTypeBlink);
+ if (permissionTypeQt == QWebEnginePermission::PermissionType::Unsupported)
return;
auto &permissionsForToken = m_transientPermissions[token];
for (auto &p : permissionsForToken) {
- if (get<0>(p) == requesting_origin && get<1>(p) == permission) {
+ if (get<0>(p) == requesting_origin && get<1>(p) == permissionTypeBlink) {
get<2>(p) = granted;
return;
}
}
- permissionsForToken.push_back({requesting_origin, permission, granted});
+ permissionsForToken.push_back({requesting_origin, permissionTypeBlink, granted});
// Render frame hosts get discarded often, so the map will eventualy fill up with junk unless
// periodically cleaned. The number 25 was chosen arbitrarily.
if (++m_transientWriteCount > 25) {
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
- base::BindOnce([](PermissionManagerQt *p){
+ base::BindOnce([](PermissionManagerQt *p)
+ {
for (auto i = p->m_transientPermissions.begin(); i != p->m_transientPermissions.end(); ++i) {
if (content::RenderFrameHost::FromFrameToken(i->first) == nullptr) {
i = p->m_transientPermissions.erase(i);
@@ -695,17 +879,18 @@ void PermissionManagerQt::setTransientPermission(blink::PermissionType permissio
}
}
-void PermissionManagerQt::resetTransientPermission(blink::PermissionType permission,
+void PermissionManagerQt::resetTransientPermission(
+ blink::PermissionType permissionTypeBlink,
const GURL& requesting_origin,
content::GlobalRenderFrameHostToken token)
{
- const QWebEnginePermission::PermissionType permissionType = toQt(permission);
- if (permissionType == QWebEnginePermission::PermissionType::Unsupported)
+ const QWebEnginePermission::PermissionType permissionTypeQt = toQt(permissionTypeBlink);
+ if (permissionTypeQt == QWebEnginePermission::PermissionType::Unsupported)
return;
auto &permissionsForToken = m_transientPermissions[token];
for (auto i = permissionsForToken.begin(); i != permissionsForToken.end(); ++i) {
- if (get<0>(*i) == requesting_origin && get<1>(*i) == permission) {
+ if (get<0>(*i) == requesting_origin && get<1>(*i) == permissionTypeBlink) {
permissionsForToken.erase(i);
return;
}
diff --git a/src/core/permission_manager_qt.h b/src/core/permission_manager_qt.h
index 7468e9861..d8474d1e1 100644
--- a/src/core/permission_manager_qt.h
+++ b/src/core/permission_manager_qt.h
@@ -6,11 +6,13 @@
#include "base/functional/callback.h"
#include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/media_stream_request.h"
#include "content/public/browser/permission_controller_delegate.h"
#include "content/public/browser/render_frame_host.h"
#include <QtWebEngineCore/qwebenginepermission.h>
#include "profile_adapter.h"
+#include "web_contents_adapter_client.h"
#include <map>
#include <tuple>
@@ -25,14 +27,30 @@ public:
PermissionManagerQt(ProfileAdapter *adapter);
~PermissionManagerQt();
+ static content::GlobalRenderFrameHostToken deserializeToken(int childId, const std::string &serializedToken);
+
+ void setPermission(
+ const QUrl &origin,
+ const QWebEnginePermission::PermissionType permissionType,
+ const QWebEnginePermission::State state,
+ const content::GlobalRenderFrameHostToken &frameToken);
+
void setPermission(
const QUrl &origin,
- QWebEnginePermission::PermissionType permissionType,
- QWebEnginePermission::State state,
- content::RenderFrameHost *rfh = nullptr);
- QWebEnginePermission::State getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
- content::RenderFrameHost *rfh = nullptr);
- QList<QWebEnginePermission> listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType);
+ const QWebEnginePermission::PermissionType permissionType,
+ const QWebEnginePermission::State state,
+ int childId, const std::string &serializedToken);
+
+ QWebEnginePermission::State getPermissionState(const QUrl &origin, const QWebEnginePermission::PermissionType permissionType,
+ const content::GlobalRenderFrameHostToken &frameToken);
+ QList<QWebEnginePermission> listPermissions(const QUrl &origin, const QWebEnginePermission::PermissionType permissionType);
+
+ void requestMediaPermissions(
+ content::RenderFrameHost *render_frame_host,
+ const WebContentsAdapterClient::MediaRequestFlags flags,
+ base::OnceCallback<void(WebContentsAdapterClient::MediaRequestFlags authorizationFlags)> callback);
+
+ void onCrossOriginNavigation(content::RenderFrameHost *render_frame_host);
void commit();
@@ -42,7 +60,6 @@ public:
const GURL& requesting_origin,
const GURL& embedding_origin) override;
-
content::PermissionStatus GetPermissionStatusForCurrentDocument(blink::PermissionType, content::RenderFrameHost*, bool) override;
blink::mojom::PermissionStatus GetPermissionStatusForWorker(blink::PermissionType, content::RenderProcessHost *, const GURL &) override;
@@ -85,6 +102,12 @@ private:
base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback;
};
+ void setPermissionImpl(
+ const QUrl &origin,
+ const QWebEnginePermission::PermissionType permissionType,
+ const QWebEnginePermission::State state,
+ const content::GlobalRenderFrameHostToken &frameToken);
+
blink::mojom::PermissionStatus getTransientPermissionStatus(blink::PermissionType permission,
const GURL& requesting_origin,
content::GlobalRenderFrameHostToken token);
diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp
index f0cabc088..1e07f95f3 100644
--- a/src/core/profile_adapter.cpp
+++ b/src/core/profile_adapter.cpp
@@ -33,7 +33,9 @@
#include "renderer_host/user_resource_controller_host.h"
#include "type_conversion.h"
#include "visited_links_manager_qt.h"
+#include "web_contents_adapter.h"
#include "web_contents_adapter_client.h"
+#include "web_contents_delegate_qt.h"
#include "web_engine_context.h"
#include <QCoreApplication>
@@ -627,15 +629,29 @@ UserResourceControllerHost *ProfileAdapter::userResourceController()
}
void ProfileAdapter::setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
- QWebEnginePermission::State state, content::RenderFrameHost *rfh)
+ QWebEnginePermission::State state, int childId, const std::string &serializedToken)
{
- static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->setPermission(origin, permissionType, state, rfh);
+ auto token = PermissionManagerQt::deserializeToken(childId, serializedToken);
+
+ // Check if the frame token is valid, and defer to WebContentsAdapter if so
+ auto *rfh = content::RenderFrameHost::FromFrameToken(token);
+ if (rfh) {
+ static_cast<WebContentsDelegateQt *>(content::WebContents::FromRenderFrameHost(rfh)->GetDelegate())
+ ->webContentsAdapter()
+ ->setPermission(origin, permissionType, state, childId, serializedToken);
+ return;
+ }
+
+ // Otherwise, set the permission directly
+ static_cast<PermissionManagerQt *>(profile()->GetPermissionControllerDelegate())
+ ->setPermission(origin, permissionType, state, token);
}
QWebEnginePermission::State ProfileAdapter::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
- content::RenderFrameHost *rfh)
+ int childId, const std::string &serializedToken)
{
- return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->getPermissionState(origin, permissionType, rfh);
+ return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())
+ ->getPermissionState(origin, permissionType, PermissionManagerQt::deserializeToken(childId, serializedToken));
}
QList<QWebEnginePermission> ProfileAdapter::listPermissions(const QUrl &origin, QWebEnginePermission::PermissionType permissionType)
diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h
index 36d286851..22dd65973 100644
--- a/src/core/profile_adapter.h
+++ b/src/core/profile_adapter.h
@@ -16,6 +16,7 @@
#define PROFILE_ADAPTER_H
#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
+#include <QtWebEngineCore/private/qwebenginepermission_p.h>
#include <QHash>
#include <QList>
@@ -187,9 +188,9 @@ public:
UserResourceControllerHost *userResourceController();
void setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
- QWebEnginePermission::State state, content::RenderFrameHost *rfh = nullptr);
+ QWebEnginePermission::State state, int childId = -1, const std::string &serializedToken = std::string());
QWebEnginePermission::State getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
- content::RenderFrameHost *rfh = nullptr);
+ int childId = -1, const std::string &serializedToken = std::string());
QList<QWebEnginePermission> listPermissions(const QUrl &origin = QUrl(),
QWebEnginePermission::PermissionType permissionType = QWebEnginePermission::PermissionType::Unsupported);
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 8c4d01e58..685b2acfe 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -16,6 +16,7 @@
#include "find_text_helper.h"
#include "media_capture_devices_dispatcher.h"
#include "pdf_util_qt.h"
+#include "permission_manager_qt.h"
#include "profile_adapter.h"
#include "profile_qt.h"
#include "qwebengineloadinginfo.h"
@@ -1417,17 +1418,18 @@ QSizeF WebContentsAdapter::lastContentsSize() const
return QSizeF();
}
-void WebContentsAdapter::setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state)
+void WebContentsAdapter::setPermission(
+ const QUrl &origin,
+ QWebEnginePermission::PermissionType permissionType,
+ QWebEnginePermission::State state,
+ int childId, const std::string &serializedToken)
{
+ auto *manager = static_cast<PermissionManagerQt*>(m_profileAdapter->profile()->GetPermissionControllerDelegate());
+
if (QWebEnginePermission::isPersistent(permissionType)) {
// Do not check for initialization in this path so permissions can be set before first navigation
Q_ASSERT(m_profileAdapter);
- if (!isInitialized()) {
- m_profileAdapter->setPermission(origin, permissionType, state);
- } else {
- m_profileAdapter->setPermission(origin, permissionType, state, m_webContents.get()->GetPrimaryMainFrame());
- }
-
+ manager->setPermission(origin, permissionType, state, childId, serializedToken);
return;
}
@@ -1440,115 +1442,87 @@ void WebContentsAdapter::setPermission(const QUrl &origin, QWebEnginePermission:
// Do nothing
break;
case QWebEnginePermission::State::Denied:
- grantMouseLockPermission(origin, false);
+ grantMouseLockPermission(origin, childId, serializedToken, false);
break;
case QWebEnginePermission::State::Granted:
- grantMouseLockPermission(origin, true);
+ grantMouseLockPermission(origin, childId, serializedToken, true);
break;
}
return;
}
- const WebContentsAdapterClient::MediaRequestFlags audioVideoCaptureFlags(
- WebContentsAdapterClient::MediaVideoCapture |
- WebContentsAdapterClient::MediaAudioCapture);
- const WebContentsAdapterClient::MediaRequestFlags desktopAudioVideoCaptureFlags(
- WebContentsAdapterClient::MediaDesktopVideoCapture |
- WebContentsAdapterClient::MediaDesktopAudioCapture);
-
- switch (state) {
- case QWebEnginePermission::State::Invalid:
- case QWebEnginePermission::State::Ask:
- // Do nothing
- return;
- case QWebEnginePermission::State::Denied:
- // Deny all media access
- grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaNone);
- return;
- case QWebEnginePermission::State::Granted:
- // Enable only the requested capture type
- break;
- }
+ // If we reach this, we must be handling media access permissions
+ manager->setPermission(origin, permissionType, state, childId, serializedToken);
- switch (permissionType) {
- case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
- grantMediaAccessPermission(origin, audioVideoCaptureFlags);
- break;
- case QWebEnginePermission::PermissionType::MediaAudioCapture:
- grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaAudioCapture);
- break;
- case QWebEnginePermission::PermissionType::MediaVideoCapture:
- grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaVideoCapture);
- break;
- case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
- grantMediaAccessPermission(origin, desktopAudioVideoCaptureFlags);
- break;
- case QWebEnginePermission::PermissionType::DesktopVideoCapture:
- grantMediaAccessPermission(origin, WebContentsAdapterClient::MediaDesktopVideoCapture);
- break;
- default:
- Q_UNREACHABLE();
- break;
+ WebContentsAdapterClient::MediaRequestFlags flags = WebContentsAdapterClient::MediaNone;
+ if (state == QWebEnginePermission::State::Granted) {
+ switch (permissionType) {
+ case QWebEnginePermission::PermissionType::MediaAudioCapture:
+ flags.setFlag(WebContentsAdapterClient::MediaAudioCapture);
+ break;
+ case QWebEnginePermission::PermissionType::MediaVideoCapture:
+ flags.setFlag(WebContentsAdapterClient::MediaVideoCapture);
+ break;
+ case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+ flags.setFlag(WebContentsAdapterClient::MediaAudioCapture);
+ flags.setFlag(WebContentsAdapterClient::MediaVideoCapture);
+ break;
+ case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+ flags.setFlag(WebContentsAdapterClient::MediaDesktopVideoCapture);
+ break;
+ case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+ flags.setFlag(WebContentsAdapterClient::MediaDesktopAudioCapture);
+ flags.setFlag(WebContentsAdapterClient::MediaDesktopVideoCapture);
+ break;
+ default:
+ break;
+ }
}
-}
-QWebEnginePermission::State WebContentsAdapter::getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType)
-{
- return m_profileAdapter->getPermissionState(origin, permissionType, m_webContents.get()->GetPrimaryMainFrame());
-}
-
-void WebContentsAdapter::grantMediaAccessPermission(const QUrl &origin, WebContentsAdapterClient::MediaRequestFlags flags)
-{
- CHECK_INITIALIZED();
- // Let the permission manager remember the reply.
- if (flags & WebContentsAdapterClient::MediaAudioCapture)
- m_profileAdapter->setPermission(origin,
- QWebEnginePermission::PermissionType::MediaAudioCapture,
- QWebEnginePermission::State::Granted,
- m_webContents.get()->GetPrimaryMainFrame());
- if (flags & WebContentsAdapterClient::MediaVideoCapture)
- m_profileAdapter->setPermission(origin,
- QWebEnginePermission::PermissionType::MediaVideoCapture,
- QWebEnginePermission::State::Granted,
- m_webContents.get()->GetPrimaryMainFrame());
MediaCaptureDevicesDispatcher::GetInstance()->handleMediaAccessPermissionResponse(m_webContents.get(), origin, flags);
}
-void WebContentsAdapter::grantMouseLockPermission(const QUrl &securityOrigin, bool granted)
+void WebContentsAdapter::grantMouseLockPermission(const QUrl &securityOrigin, int childId,
+ const std::string &serializedToken, bool granted)
{
CHECK_INITIALIZED();
- if (securityOrigin != toQt(m_webContents->GetLastCommittedURL().DeprecatedGetOriginAsURL()))
- return;
- if (granted) {
- if (RenderWidgetHostViewQt *rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) {
- rwhv->Focus();
- if (!rwhv->HasFocus()) {
- // We tried to activate our RWHVQtDelegate, but we failed. This probably means that
- // the permission was granted from a modal dialog and the windowing system is not ready
- // to set focus on the originating view. Since pointer lock strongly requires it, we just
- // wait until the next FocusIn event.
- m_pendingMouseLockPermissions.insert(securityOrigin, granted);
- return;
- }
- } else
- granted = false;
+ bool focused = false;
+ if (RenderWidgetHostViewQt *rwhv = static_cast<RenderWidgetHostViewQt *>(m_webContents->GetRenderWidgetHostView())) {
+ rwhv->Focus();
+ if (rwhv->HasFocus()) {
+ focused = true;
+ }
+ } else {
+ granted = false;
}
- m_webContents->GotResponseToPointerLockRequest(granted ? blink::mojom::PointerLockResult::kSuccess
- : blink::mojom::PointerLockResult::kPermissionDenied);
+ m_pendingMouseLockPermissions.enqueue({ securityOrigin, granted, childId, serializedToken });
+
+ if (focused) {
+ handlePendingMouseLockPermission();
+ }
}
void WebContentsAdapter::handlePendingMouseLockPermission()
{
CHECK_INITIALIZED();
- auto it = m_pendingMouseLockPermissions.find(toQt(m_webContents->GetLastCommittedURL().DeprecatedGetOriginAsURL()));
- if (it != m_pendingMouseLockPermissions.end()) {
- m_webContents->GotResponseToPointerLockRequest(it.value() ? blink::mojom::PointerLockResult::kSuccess
- : blink::mojom::PointerLockResult::kPermissionDenied);
- m_pendingMouseLockPermissions.erase(it);
- }
+ if (!m_pendingMouseLockPermissions.size())
+ return;
+
+ auto pending = m_pendingMouseLockPermissions.dequeue();
+
+ // Simply set the permission in the manager. The callback from WebContentsDelegateQt::RequestPointerLock()
+ // will ensure WebContents receives the response
+ auto *manager = static_cast<PermissionManagerQt*>(m_profileAdapter->profile()->GetPermissionControllerDelegate());
+ manager->setPermission(
+ get<0>(pending), // origin
+ QWebEnginePermission::PermissionType::MouseLock,
+ get<1>(pending) // granted
+ ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Denied,
+ get<2>(pending), // childId
+ get<3>(pending)); // serializedToken
}
void WebContentsAdapter::setBackgroundColor(const QColor &color)
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 3bb639b1b..212411109 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -21,6 +21,7 @@
#include <QtCore/QUrl>
#include <QtCore/QVariant>
#include <QtCore/QPointer>
+#include <QtCore/QQueue>
#include <QtGui/qtgui-config.h>
#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
#include <QtWebEngineCore/qwebenginecontextmenurequest.h>
@@ -48,6 +49,7 @@ namespace content {
class WebContents;
class SiteInstance;
class RenderFrameHost;
+struct GlobalRenderFrameHostToken;
}
QT_BEGIN_NAMESPACE
@@ -179,11 +181,9 @@ public:
void devToolsFrontendDestroyed(DevToolsFrontendQt *frontend);
QString devToolsId();
- void setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state);
- QWebEnginePermission::State getPermissionState(const QUrl &origin, QWebEnginePermission::PermissionType permissionType);
-
- void grantMediaAccessPermission(const QUrl &origin, WebContentsAdapterClient::MediaRequestFlags flags);
- void grantMouseLockPermission(const QUrl &origin, bool granted);
+ void setPermission(const QUrl &origin, QWebEnginePermission::PermissionType permissionType,
+ QWebEnginePermission::State state, int childId = -1, const std::string &serializedToken = std::string());
+ void grantMouseLockPermission(const QUrl &origin, int childId, const std::string &serializedToken, bool granted);
void handlePendingMouseLockPermission();
void setBackgroundColor(const QColor &color);
@@ -272,7 +272,7 @@ private:
#endif
WebContentsAdapterClient *m_adapterClient;
quint64 m_nextRequestId;
- QMap<QUrl, bool> m_pendingMouseLockPermissions;
+ QQueue<std::tuple<QUrl, bool, int, std::string>> m_pendingMouseLockPermissions;
QMap<quint64, std::function<void(const QVariant &)>> m_javaScriptCallbacks;
std::map<quint64, std::function<void(QSharedPointer<QByteArray>)>> m_printCallbacks;
std::unique_ptr<content::DropData> m_currentDropData;
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 9ce5bebfc..2f93d4783 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -16,6 +16,7 @@
#define WEB_CONTENTS_ADAPTER_CLIENT_H
#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h>
+#include <QtWebEngineCore/private/qwebenginepermission_p.h>
#include <QtWebEngineCore/qwebenginepermission.h>
#include "profile_adapter.h"
@@ -195,9 +196,8 @@ public:
virtual QObject *accessibilityParentObject() = 0;
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) = 0;
virtual void authenticationRequired(QSharedPointer<AuthenticationDialogController>) = 0;
- virtual void runFeaturePermissionRequest(QWebEnginePermission::PermissionType, const QUrl &securityOrigin) = 0;
- virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) = 0;
- virtual void runMouseLockPermissionRequest(const QUrl &securityOrigin) = 0;
+ virtual void runFeaturePermissionRequest(QWebEnginePermission::PermissionType, const QUrl &securityOrigin,
+ int childId, const std::string &serializedToken) = 0;
virtual void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) = 0;
virtual void runFileSystemAccessRequest(QWebEngineFileSystemAccessRequest) = 0;
virtual QWebEngineSettings *webEngineSettings() const = 0;
@@ -229,7 +229,6 @@ public:
virtual WebContentsAdapter* webContentsAdapter() = 0;
virtual void releaseProfile() = 0;
virtual void showWebAuthDialog(QWebEngineWebAuthUxRequest *request) = 0;
- virtual QWebEnginePermission createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) = 0;
};
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp
index 89a0a6582..77ba5ec91 100644
--- a/src/core/web_contents_delegate_qt.cpp
+++ b/src/core/web_contents_delegate_qt.cpp
@@ -19,6 +19,7 @@
#include "javascript_dialog_manager_qt.h"
#include "media_capture_devices_dispatcher.h"
#include "native_web_keyboard_event_qt.h"
+#include "permission_manager_qt.h"
#include "profile_adapter.h"
#include "profile_qt.h"
#include "qwebengineloadinginfo.h"
@@ -418,6 +419,12 @@ void WebContentsDelegateQt::emitLoadCommitted()
void WebContentsDelegateQt::DidFinishNavigation(content::NavigationHandle *navigation_handle)
{
+ if (navigation_handle->HasCommitted() && !navigation_handle->IsSameOrigin()) {
+ PermissionManagerQt *permissionManager = static_cast<PermissionManagerQt *>(
+ navigation_handle->GetWebContents()->GetBrowserContext()->GetPermissionControllerDelegate());
+ permissionManager->onCrossOriginNavigation(navigation_handle->GetRenderFrameHost());
+ }
+
if (!navigation_handle->IsInMainFrame())
return;
@@ -731,14 +738,32 @@ void WebContentsDelegateQt::ActivateContents(content::WebContents* contents)
void WebContentsDelegateQt::RequestPointerLock(content::WebContents *web_contents, bool user_gesture, bool last_unlocked_by_target)
{
- Q_UNUSED(user_gesture);
-
if (last_unlocked_by_target)
web_contents->GotResponseToPointerLockRequest(blink::mojom::PointerLockResult::kSuccess);
- else
- m_viewClient->runMouseLockPermissionRequest(toQt(web_contents->GetLastCommittedURL().DeprecatedGetOriginAsURL()));
+ else {
+ PermissionManagerQt *permissionManager = static_cast<PermissionManagerQt *>(
+ web_contents->GetBrowserContext()->GetPermissionControllerDelegate());
+
+ auto *rfh = web_contents->GetFocusedFrame();
+ if (!rfh)
+ rfh = web_contents->GetPrimaryMainFrame();
+
+ permissionManager->RequestPermissions(
+ rfh,
+ content::PermissionRequestDescription(blink::PermissionType::POINTER_LOCK, user_gesture, rfh->GetLastCommittedOrigin().GetURL()),
+ base::BindOnce([](content::WebContents *web_contents, PermissionManagerQt *manager, const std::vector<blink::mojom::PermissionStatus> &status)
+ {
+ Q_ASSERT(status.size() == 1);
+
+ web_contents->GotResponseToPointerLockRequest(status[0] == blink::mojom::PermissionStatus::GRANTED
+ ? blink::mojom::PointerLockResult::kSuccess
+ : blink::mojom::PointerLockResult::kPermissionDenied);
+ }, web_contents, permissionManager)
+ );
+ }
}
+
void WebContentsDelegateQt::overrideWebPreferences(content::WebContents *webContents, blink::web_pref::WebPreferences *webPreferences)
{
WebEngineSettings::get(m_viewClient->webEngineSettings())->overrideWebPreferences(webContents, webPreferences);
@@ -773,9 +798,12 @@ void WebContentsDelegateQt::selectClientCert(const QSharedPointer<ClientCertSele
m_viewClient->selectClientCert(selectController);
}
-void WebContentsDelegateQt::requestFeaturePermission(QWebEnginePermission::PermissionType permissionType, const QUrl &requestingOrigin)
+void WebContentsDelegateQt::requestFeaturePermission(
+ QWebEnginePermission::PermissionType permissionType,
+ const QUrl &requestingOrigin,
+ const content::GlobalRenderFrameHostToken &frameToken)
{
- m_viewClient->runFeaturePermissionRequest(permissionType, requestingOrigin);
+ m_viewClient->runFeaturePermissionRequest(permissionType, requestingOrigin, frameToken.child_id, frameToken.frame_token.ToString());
}
extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition);
@@ -834,18 +862,22 @@ bool WebContentsDelegateQt::CheckMediaAccessPermission(content::RenderFrameHost
blink::mojom::MediaStreamType type)
{
Q_ASSERT(rfh);
+
+ auto token = rfh->GetGlobalFrameToken();
+ std::string serializedToken = token.frame_token.ToString();
+
switch (type) {
case blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE:
return m_viewClient->profileAdapter()->getPermissionState(
toQt(security_origin),
QWebEnginePermission::PermissionType::MediaAudioCapture,
- rfh)
+ token.child_id, serializedToken)
== QWebEnginePermission::State::Granted;
case blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE:
return m_viewClient->profileAdapter()->getPermissionState(
toQt(security_origin),
QWebEnginePermission::PermissionType::MediaVideoCapture,
- rfh)
+ token.child_id, serializedToken)
== QWebEnginePermission::State::Granted;
default:
LOG(INFO) << "WebContentsDelegateQt::CheckMediaAccessPermission: "
diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h
index 099d8280b..383803c4d 100644
--- a/src/core/web_contents_delegate_qt.h
+++ b/src/core/web_contents_delegate_qt.h
@@ -25,6 +25,7 @@ class ColorChooser;
class JavaScriptDialogManager;
class WebContents;
struct MediaStreamRequest;
+struct GlobalRenderFrameHostToken;
}
namespace QtWebEngineCore {
@@ -144,7 +145,7 @@ public:
void overrideWebPreferences(content::WebContents *, blink::web_pref::WebPreferences*);
void allowCertificateError(const QSharedPointer<CertificateErrorController> &);
void selectClientCert(const QSharedPointer<ClientCertSelectController> &);
- void requestFeaturePermission(QWebEnginePermission::PermissionType permissionType, const QUrl &requestingOrigin);
+ void requestFeaturePermission(QWebEnginePermission::PermissionType permissionType, const QUrl &requestingOrigin, const content::GlobalRenderFrameHostToken &frameToken);
void launchExternalURL(const QUrl &url, ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture);
FindTextHelper *findTextHelper();
diff --git a/src/webenginequick/api/qquickwebengineprofile.cpp b/src/webenginequick/api/qquickwebengineprofile.cpp
index dce3c8822..80e4ec5ac 100644
--- a/src/webenginequick/api/qquickwebengineprofile.cpp
+++ b/src/webenginequick/api/qquickwebengineprofile.cpp
@@ -1229,7 +1229,7 @@ QWebEnginePermission QQuickWebEngineProfile::queryPermission(const QUrl &securit
return QWebEnginePermission(new QWebEnginePermissionPrivate());
}
- auto *pvt = new QWebEnginePermissionPrivate(securityOrigin, permissionType, nullptr, d->profileAdapter());
+ auto *pvt = new QWebEnginePermissionPrivate(securityOrigin, permissionType, d->profileAdapter());
return QWebEnginePermission(pvt);
}
diff --git a/src/webenginequick/api/qquickwebengineprofile.h b/src/webenginequick/api/qquickwebengineprofile.h
index 899d431a1..0995538be 100644
--- a/src/webenginequick/api/qquickwebengineprofile.h
+++ b/src/webenginequick/api/qquickwebengineprofile.h
@@ -44,7 +44,7 @@ class Q_WEBENGINEQUICK_EXPORT QQuickWebEngineProfile : public QObject {
Q_PROPERTY(bool isPushServiceEnabled READ isPushServiceEnabled WRITE setPushServiceEnabled NOTIFY pushServiceEnabledChanged FINAL REVISION(6,5))
Q_PROPERTY(QWebEngineClientHints *clientHints READ clientHints FINAL REVISION(6,8))
#if QT_CONFIG(webengine_extensions)
- Q_PROPERTY(QWebEngineExtensionManager *extensionManager READ extensionManager REVISION(6, 10))
+ Q_PROPERTY(QWebEngineExtensionManager *extensionManager READ extensionManager CONSTANT REVISION(6, 10))
#endif
QML_NAMED_ELEMENT(WebEngineProfile)
QML_ADDED_IN_VERSION(1, 1)
diff --git a/src/webenginequick/api/qquickwebengineview.cpp b/src/webenginequick/api/qquickwebengineview.cpp
index ade8b451c..619cbaef6 100644
--- a/src/webenginequick/api/qquickwebengineview.cpp
+++ b/src/webenginequick/api/qquickwebengineview.cpp
@@ -521,22 +521,30 @@ static QQuickWebEngineView::Feature toDeprecatedFeature(QWebEnginePermission::Pe
QT_WARNING_POP
#endif // QT_DEPRECATED_SINCE(6, 8)
-void QQuickWebEngineViewPrivate::runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin)
+void QQuickWebEngineViewPrivate::runFeaturePermissionRequest(
+ QWebEnginePermission::PermissionType permissionType,
+ const QUrl &securityOrigin,
+ int childId, const std::string &serializedToken)
{
Q_Q(QQuickWebEngineView);
- if (QWebEnginePermission::isPersistent(permissionType)) {
- Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
-#if QT_DEPRECATED_SINCE(6, 8)
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_DEPRECATED
- Q_EMIT q->featurePermissionRequested(securityOrigin, toDeprecatedFeature(permissionType));
- QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 8)
+ if (permissionType == QWebEnginePermission::PermissionType::MouseLock) {
+ // Not supported in Qt Quick
+ auto permission = QWebEnginePermission(
+ new QWebEnginePermissionPrivate(securityOrigin, permissionType, profileAdapter(), childId, serializedToken));
+ permission.deny();
return;
}
- Q_UNREACHABLE();
+ Q_EMIT q->permissionRequested(QWebEnginePermission(
+ new QWebEnginePermissionPrivate(securityOrigin, permissionType, profileAdapter(), childId, serializedToken)));
+#if QT_DEPRECATED_SINCE(6, 8)
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_DEPRECATED
+ Q_EMIT q->featurePermissionRequested(securityOrigin, toDeprecatedFeature(permissionType));
+ QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 8)
+ return;
}
void QQuickWebEngineViewPrivate::showColorDialog(QSharedPointer<ColorChooserController> controller)
@@ -809,54 +817,6 @@ void QQuickWebEngineViewPrivate::authenticationRequired(QSharedPointer<Authentic
ui()->showDialog(controller);
}
-void QQuickWebEngineViewPrivate::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags)
-{
- Q_Q(QQuickWebEngineView);
- if (!requestFlags)
- return;
- QWebEnginePermission::PermissionType permissionType;
- if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture) && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::MediaAudioVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
- permissionType = QWebEnginePermission::PermissionType::MediaAudioCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::MediaVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture) &&
- requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::DesktopAudioVideoCapture;
- else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- permissionType = QWebEnginePermission::PermissionType::DesktopVideoCapture;
- Q_EMIT q->permissionRequested(createFeaturePermissionObject(securityOrigin, permissionType));
-
-#if QT_DEPRECATED_SINCE(6, 8)
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_DEPRECATED
- QQuickWebEngineView::Feature deprecatedFeature;
-
- if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)
- && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- deprecatedFeature = QQuickWebEngineView::MediaAudioVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture))
- deprecatedFeature = QQuickWebEngineView::MediaAudioCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture))
- deprecatedFeature = QQuickWebEngineView::MediaVideoCapture;
- else if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture)
- && requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- deprecatedFeature = QQuickWebEngineView::DesktopAudioVideoCapture;
- else // if (requestFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture))
- deprecatedFeature = QQuickWebEngineView::DesktopVideoCapture;
-
- Q_EMIT q->featurePermissionRequested(securityOrigin, deprecatedFeature);
- QT_WARNING_POP
-#endif // QT_DEPRECATED_SINCE(6, 8)
-}
-
-void QQuickWebEngineViewPrivate::runMouseLockPermissionRequest(const QUrl &securityOrigin)
-{
- // TODO: Add mouse lock support
- adapter->grantMouseLockPermission(securityOrigin, false);
-}
-
void QQuickWebEngineViewPrivate::runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest request)
{
Q_Q(QQuickWebEngineView);
@@ -1523,12 +1483,6 @@ void QQuickWebEngineViewPrivate::showWebAuthDialog(QWebEngineWebAuthUxRequest *r
Q_EMIT q->webAuthUxRequested(request);
}
-QWebEnginePermission QQuickWebEngineViewPrivate::createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType)
-{
- auto *returnPrivate = new QWebEnginePermissionPrivate(securityOrigin, permissionType, adapter, profileAdapter());
- return QWebEnginePermission(returnPrivate);
-}
-
bool QQuickWebEngineView::isLoading() const
{
Q_D(const QQuickWebEngineView);
diff --git a/src/webenginequick/api/qquickwebengineview_p_p.h b/src/webenginequick/api/qquickwebengineview_p_p.h
index d78157597..8fe98145b 100644
--- a/src/webenginequick/api/qquickwebengineview_p_p.h
+++ b/src/webenginequick/api/qquickwebengineview_p_p.h
@@ -105,8 +105,6 @@ public:
bool passOnFocus(bool reverse) override;
void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override;
void authenticationRequired(QSharedPointer<QtWebEngineCore::AuthenticationDialogController>) override;
- void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) override;
- void runMouseLockPermissionRequest(const QUrl &securityOrigin) override;
void runRegisterProtocolHandlerRequest(QWebEngineRegisterProtocolHandlerRequest) override;
void runFileSystemAccessRequest(QWebEngineFileSystemAccessRequest) override;
QObject *accessibilityParentObject() override;
@@ -114,7 +112,8 @@ public:
void allowCertificateError(const QWebEngineCertificateError &error) override;
void selectClientCert(const QSharedPointer<QtWebEngineCore::ClientCertSelectController>
&selectController) override;
- void runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin) override;
+ void runFeaturePermissionRequest(QWebEnginePermission::PermissionType permissionType, const QUrl &securityOrigin,
+ int childId, const std::string &serializedToken) override;
void renderProcessTerminated(RenderProcessTerminationStatus terminationStatus, int exitCode) override;
void requestGeometryChange(const QRect &geometry, const QRect &frameGeometry) override;
void updateScrollPosition(const QPointF &position) override;
@@ -139,7 +138,6 @@ public:
const QRect &bounds, bool autoselectFirstSuggestion) override;
void hideAutofillPopup() override;
void showWebAuthDialog(QWebEngineWebAuthUxRequest *request) override;
- QWebEnginePermission createFeaturePermissionObject(const QUrl &securityOrigin, QWebEnginePermission::PermissionType permissionType) override;
void updateAction(QQuickWebEngineView::WebAction) const;
bool adoptWebContents(QtWebEngineCore::WebContentsAdapter *webContents);
diff --git a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
index c176be70e..d4d6cab99 100644
--- a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
+++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp
@@ -109,7 +109,7 @@ void tst_QWebEngineFrame::htmlName()
QWebEnginePage page;
QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) };
page.load(QUrl("qrc:/resources/iframes.html"));
- QTRY_COMPARE(loadSpy.size(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 10000);
auto children = page.mainFrame().children();
QCOMPARE(children.at(0).name(), "test-subframe0");
QCOMPARE(children.at(0).htmlName(), "iframe0-300x200");
diff --git a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml
index ebb49f9df..a56584230 100644
--- a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml
+++ b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml
@@ -75,6 +75,9 @@ TestWebEngineView {
rejectPendingRequest()
tryVerify(jsPromiseRejected)
+ resetRequestState()
+ wait(1000)
+
// 2. Accepting request on QML side should either fulfill or reject the
// Promise on JS side. Due to the potential lack of physical media devices
// deeper in the content layer we cannot guarantee that the promise will
@@ -85,11 +88,16 @@ TestWebEngineView {
acceptPendingRequest()
tryVerify(jsPromiseSettled)
+ resetRequestState()
+ wait(1000)
+
// 3. Media feature permissions are not remembered.
jsGetUserMedia(row.constraints);
verifyPermissionType(row.feature)
acceptPendingRequest()
tryVerify(jsPromiseSettled)
+
+ resetRequestState()
}
}
@@ -158,10 +166,12 @@ TestWebEngineView {
function acceptPendingRequest() {
if (permissionObject)
permissionObject.grant()
- resetRequestState()
}
function resetRequestState() {
+ if (permissionObject)
+ permissionObject.reset()
+
permissionObject = undefined
isDesktopMediaRequestHandled = false
gotEmptyDesktopMediaRequest = false
@@ -170,7 +180,6 @@ TestWebEngineView {
function rejectPendingRequest() {
if (permissionObject)
permissionObject.deny()
- resetRequestState()
}
////
diff --git a/tests/auto/util/util.h b/tests/auto/util/util.h
index 65e7fb8b5..6dc420194 100644
--- a/tests/auto/util/util.h
+++ b/tests/auto/util/util.h
@@ -134,6 +134,13 @@ static inline QVariant evaluateJavaScriptSync(QWebEnginePage *page, const QStrin
return spy.waitForResult();
}
+static inline QVariant evaluateJavaScriptSync(QWebEngineFrame *frame, const QString &script)
+{
+ CallbackSpy<QVariant> spy;
+ frame->runJavaScript(script, spy.ref());
+ return spy.waitForResult();
+}
+
static inline QVariant evaluateJavaScriptSyncInWorld(QWebEnginePage *page, const QString &script, int worldId)
{
CallbackSpy<QVariant> spy;
diff --git a/tests/auto/widgets/CMakeLists.txt b/tests/auto/widgets/CMakeLists.txt
index e31ff2170..2195dd5e6 100644
--- a/tests/auto/widgets/CMakeLists.txt
+++ b/tests/auto/widgets/CMakeLists.txt
@@ -3,6 +3,7 @@
add_subdirectory(defaultsurfaceformat)
add_subdirectory(qwebenginepage)
+add_subdirectory(qwebenginepermission)
add_subdirectory(qwebengineprofile)
add_subdirectory(qwebengineprofilebuilder)
add_subdirectory(qwebengineview)
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 6df52dc61..10851580e 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -128,10 +128,6 @@ private Q_SLOTS:
void acceptNavigationRequestWithFormData();
void acceptNavigationRequestNavigationType();
void acceptNavigationRequestRelativeToNothing();
-#ifndef Q_OS_MACOS
- void geolocationRequestJS_data();
- void geolocationRequestJS();
-#endif
void loadFinished();
void actionStates();
void pasteImage();
@@ -240,16 +236,8 @@ private Q_SLOTS:
void triggerActionWithoutMenu();
void dynamicFrame();
- void notificationPermission_data();
- void notificationPermission();
void sendNotification();
- void clipboardReadWritePermissionInitialState_data();
- void clipboardReadWritePermissionInitialState();
- void clipboardReadWritePermission_data();
- void clipboardReadWritePermission();
void contentsSize();
- void localFontAccessPermission_data();
- void localFontAccessPermission();
void setLifecycleState();
void setVisible();
@@ -448,79 +436,6 @@ void tst_QWebEnginePage::acceptNavigationRequest()
QCOMPARE(toPlainTextSync(&page), QString("/foo?"));
}
-class JSTestPage : public QWebEnginePage
-{
-Q_OBJECT
-public:
- JSTestPage(QObject* parent = 0)
- : QWebEnginePage(parent) {}
-
- virtual bool shouldInterruptJavaScript()
- {
- return true;
- }
-public Q_SLOTS:
- void requestPermission(QWebEnginePermission permission)
- {
- if (m_allowGeolocation)
- permission.grant();
- else
- permission.deny();
- }
-
-public:
- void setGeolocationPermission(bool allow)
- {
- m_allowGeolocation = allow;
- }
-
-private:
- bool m_allowGeolocation;
-};
-
-#ifndef Q_OS_MACOS
-void tst_QWebEnginePage::geolocationRequestJS_data()
-{
- QTest::addColumn<bool>("allowed");
- QTest::addColumn<int>("errorCode");
- QTest::newRow("allowed") << true << 0;
- QTest::newRow("not allowed") << false << 1;
-}
-
-void tst_QWebEnginePage::geolocationRequestJS()
-{
- QFETCH(bool, allowed);
- QFETCH(int, errorCode);
- QWebEngineView view;
- JSTestPage *newPage = new JSTestPage(&view);
- view.setPage(newPage);
- newPage->profile()->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
- newPage->setGeolocationPermission(allowed);
-
- connect(newPage, SIGNAL(permissionRequested(QWebEnginePermission)),
- newPage, SLOT(requestPermission(QWebEnginePermission)));
-
- QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool)));
- newPage->setHtml(QString("<html><body>test</body></html>"), QUrl("qrc://secure/origin"));
- QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.size(), 1, 20000);
-
- // Geolocation is only enabled for visible WebContents.
- view.show();
- QVERIFY(QTest::qWaitForWindowExposed(&view));
-
- if (evaluateJavaScriptSync(newPage, QLatin1String("!navigator.geolocation")).toBool())
- QSKIP("Geolocation is not supported.");
-
- evaluateJavaScriptSync(newPage, "var errorCode = 0; var done = false; function error(err) { errorCode = err.code; done = true; } function success(pos) { done = true; } navigator.geolocation.getCurrentPosition(success, error)");
-
- QTRY_VERIFY(evaluateJavaScriptSync(newPage, "done").toBool());
- int result = evaluateJavaScriptSync(newPage, "errorCode").toInt();
- if (result == 2)
- QEXPECT_FAIL("", "No location service available.", Continue);
- QCOMPARE(result, errorCode);
-}
-#endif
-
void tst_QWebEnginePage::loadFinished()
{
QWebEnginePage page;
@@ -1784,14 +1699,21 @@ public:
{
if (m_permission)
m_permission->deny();
- resetRequestState();
}
void acceptPendingRequest()
{
if (m_permission)
m_permission->grant();
- resetRequestState();
+ }
+
+ void resetRequestState()
+ {
+ m_gotDesktopMediaRequest = false;
+ m_gotEmptyDesktopMediaRequest = false;
+ if (m_permission)
+ m_permission->reset();
+ m_permission.reset();
}
bool gotExpectedRequests(bool isDesktopPermission,
@@ -1828,13 +1750,6 @@ private Q_SLOTS:
}
private:
- void resetRequestState()
- {
- m_gotDesktopMediaRequest = false;
- m_gotEmptyDesktopMediaRequest = false;
- m_permission.reset();
- }
-
void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel, const QString &message, int,
const QString &) override
{
@@ -1888,7 +1803,7 @@ void tst_QWebEnginePage::getUserMediaRequest()
QVERIFY(QTest::qWaitForWindowExposed(&view));
}
- QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 60000);
+ QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 10000);
page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
// 1. Rejecting request on C++ side should reject promise on JS side.
@@ -1897,6 +1812,9 @@ void tst_QWebEnginePage::getUserMediaRequest()
page.rejectPendingRequest();
QTRY_VERIFY(page.jsPromiseRejected());
+ page.resetRequestState();
+ QTest::qWait(1000);
+
// 2. Accepting request on C++ side should either fulfill or reject the
// Promise on JS side. Due to the potential lack of physical media devices
// deeper in the content layer we cannot guarantee that the promise will
@@ -1905,19 +1823,22 @@ void tst_QWebEnginePage::getUserMediaRequest()
page.jsGetMedia(call);
QTRY_VERIFY(page.gotExpectedRequests(isDesktopPermission, permissionType));
page.acceptPendingRequest();
- QTRY_VERIFY(page.jsPromiseSettled());
+ QTRY_VERIFY_WITH_TIMEOUT(page.jsPromiseSettled(), 10000);
+
+ page.resetRequestState();
+ QTest::qWait(1000);
// 3. Media permissions are not remembered.
page.jsGetMedia(call);
QTRY_VERIFY(page.gotExpectedRequests(isDesktopPermission, permissionType));
page.acceptPendingRequest();
- QTRY_VERIFY(page.jsPromiseSettled());
+ QTRY_VERIFY_WITH_TIMEOUT(page.jsPromiseSettled(), 10000);
}
void tst_QWebEnginePage::getUserMediaRequestDesktopAudio()
{
GetUserMediaTestPage page;
- QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000);
+ QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 10000);
page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
// Audio-only desktop capture is not supported. JS Promise should be
@@ -3848,76 +3769,6 @@ public:
}
};
-void tst_QWebEnginePage::notificationPermission_data()
-{
- QTest::addColumn<bool>("setOnInit");
- QTest::addColumn<QWebEnginePermission::State>("policy");
- QTest::addColumn<QString>("permission");
- QTest::newRow("denyOnInit") << true << QWebEnginePermission::State::Denied << "denied";
- QTest::newRow("deny") << false << QWebEnginePermission::State::Denied << "denied";
- QTest::newRow("grant") << false << QWebEnginePermission::State::Granted << "granted";
- QTest::newRow("grantOnInit") << true << QWebEnginePermission::State::Granted << "granted";
-}
-
-void tst_QWebEnginePage::notificationPermission()
-{
- QFETCH(bool, setOnInit);
- QFETCH(QWebEnginePermission::State, policy);
- QFETCH(QString, permission);
-
- QWebEngineProfile otr;
- otr.setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
- QWebEnginePage page(&otr, nullptr);
-
- QUrl baseUrl("https://www.example.com/somepage.html");
-
- bool permissionRequested = false, errorState = false;
- connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) {
- if (permission.permissionType() != QWebEnginePermission::PermissionType::Notifications)
- return;
- if (permissionRequested || permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
- qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << permission.origin();
- errorState = true;
- return;
- }
- permissionRequested = true;
-
- if (policy == QWebEnginePermission::State::Granted)
- permission.grant();
- else
- permission.deny();
- });
-
- QWebEnginePermission permissionObject = otr.queryPermission(baseUrl, QWebEnginePermission::PermissionType::Notifications);
- if (setOnInit) {
- if (policy == QWebEnginePermission::State::Granted)
- permissionObject.grant();
- else
- permissionObject.deny();
- }
-
- QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
- page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
- QTRY_COMPARE(spy.size(), 1);
-
- QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default"));
-
- if (!setOnInit) {
- if (policy == QWebEnginePermission::State::Granted)
- permissionObject.grant();
- else
- permissionObject.deny();
- QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), permission);
- }
-
- auto js = QStringLiteral("var permission; Notification.requestPermission().then(p => { permission = p })");
- evaluateJavaScriptSync(&page, js);
- QTRY_COMPARE(evaluateJavaScriptSync(&page, "permission").toString(), permission);
- // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call from JS
- QVERIFY(permissionRequested);
- QVERIFY(!errorState);
-}
-
void tst_QWebEnginePage::sendNotification()
{
NotificationPage page(QWebEnginePermission::State::Granted);
@@ -3958,180 +3809,6 @@ void tst_QWebEnginePage::sendNotification()
QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData());
}
-static QString clipboardPermissionQuery(QString variableName, QString permissionName)
-{
- return QString("var %1; navigator.permissions.query({ name:'%2' }).then((p) => { %1 = p.state; "
- "});")
- .arg(variableName)
- .arg(permissionName);
-}
-
-
-void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data()
-{
- QTest::addColumn<bool>("canAccessClipboard");
- QTest::addColumn<bool>("canPaste");
- QTest::addColumn<QString>("readPermission");
- QTest::addColumn<QString>("writePermission");
- QTest::newRow("access and paste should grant both") << true << true << "granted" << "granted";
- QTest::newRow("paste only should prompt for both") << false << true << "prompt" << "prompt";
- QTest::newRow("access only should grant for write only")
- << true << false << "prompt" << "granted";
- QTest::newRow("no access or paste should prompt for both")
- << false << false << "prompt" << "prompt";
-}
-
-void tst_QWebEnginePage::clipboardReadWritePermissionInitialState()
-{
- QFETCH(bool, canAccessClipboard);
- QFETCH(bool, canPaste);
- QFETCH(QString, readPermission);
- QFETCH(QString, writePermission);
-
- QWebEngineProfile otr;
- otr.setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
- QWebEngineView view(&otr);
- QWebEnginePage &page = *view.page();
- view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
- page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard,
- canAccessClipboard);
- page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, canPaste);
-
- QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
- QUrl baseUrl("https://www.example.com/somepage.html");
- page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
- QTRY_COMPARE(spy.size(), 1);
-
- evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
- QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), readPermission);
- evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
- QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), writePermission);
-}
-
-void tst_QWebEnginePage::clipboardReadWritePermission_data()
-{
- QTest::addColumn<bool>("canAccessClipboard");
- QTest::addColumn<QWebEnginePermission::State>("initialPolicy");
- QTest::addColumn<QString>("initialPermission");
- QTest::addColumn<QWebEnginePermission::State>("requestPolicy");
- QTest::addColumn<QString>("finalPermission");
-
- QTest::newRow("noAccessGrantGrant")
- << false << QWebEnginePermission::State::Granted << "granted"
- << QWebEnginePermission::State::Granted << "granted";
- QTest::newRow("noAccessGrantDeny")
- << false << QWebEnginePermission::State::Granted << "granted"
- << QWebEnginePermission::State::Denied << "denied";
- QTest::newRow("noAccessDenyGrant")
- << false << QWebEnginePermission::State::Denied << "denied"
- << QWebEnginePermission::State::Granted << "granted";
- QTest::newRow("noAccessDenyDeny") << false << QWebEnginePermission::State::Denied << "denied"
- << QWebEnginePermission::State::Denied << "denied";
- QTest::newRow("noAccessAskGrant") << false << QWebEnginePermission::State::Ask << "prompt"
- << QWebEnginePermission::State::Granted << "granted";
-
- // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to
- // true
- QTest::newRow("accessGrantGrant")
- << true << QWebEnginePermission::State::Granted << "granted"
- << QWebEnginePermission::State::Granted << "granted";
- QTest::newRow("accessDenyDeny") << true << QWebEnginePermission::State::Denied << "granted"
- << QWebEnginePermission::State::Denied << "granted";
- QTest::newRow("accessAskAsk") << true << QWebEnginePermission::State::Ask << "granted"
- << QWebEnginePermission::State::Ask << "granted";
-}
-
-void tst_QWebEnginePage::clipboardReadWritePermission()
-{
- QFETCH(bool, canAccessClipboard);
- QFETCH(QWebEnginePermission::State, initialPolicy);
- QFETCH(QString, initialPermission);
- QFETCH(QWebEnginePermission::State, requestPolicy);
- QFETCH(QString, finalPermission);
-
- QWebEngineProfile otr;
- otr.setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
- QWebEngineView view(&otr);
- QWebEnginePage &page = *view.page();
- view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
- page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard,
- canAccessClipboard);
- page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, true);
-
- QUrl baseUrl("https://www.example.com/somepage.html");
-
- int permissionRequestCount = 0;
- bool errorState = false;
-
- // if JavascriptCanAccessClipboard is true, this never fires
- connect(&page, &QWebEnginePage::permissionRequested, &page,
- [&](QWebEnginePermission permission) {
- if (permission.permissionType() != QWebEnginePermission::PermissionType::ClipboardReadWrite)
- return;
- if (permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
- qWarning() << "Unexpected case. Can't proceed." << permission.origin();
- errorState = true;
- return;
- }
- permissionRequestCount++;
- switch (requestPolicy) {
- case QWebEnginePermission::State::Granted:
- permission.grant();
- break;
- case QWebEnginePermission::State::Denied:
- permission.deny();
- break;
- case QWebEnginePermission::State::Ask:
- permission.reset();
- break;
- default:
- break;
- }
- });
-
- QWebEnginePermission permissionObject = otr.queryPermission(baseUrl, QWebEnginePermission::PermissionType::ClipboardReadWrite);
- switch (initialPolicy) {
- case QWebEnginePermission::State::Granted:
- permissionObject.grant();
- break;
- case QWebEnginePermission::State::Denied:
- permissionObject.deny();
- break;
- case QWebEnginePermission::State::Ask:
- permissionObject.reset();
- break;
- case QWebEnginePermission::State::Invalid:
- break;
- }
-
- QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
- page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
- QTRY_COMPARE(spy.size(), 1);
-
- evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
- QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), initialPermission);
- evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
- QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), initialPermission);
-
- auto triggerRequest = [&page](QString variableName, QString apiCall)
- {
- auto js = QString("var %1; navigator.clipboard.%2.then((v) => { %1 = 'granted' }, (v) => { %1 = "
- "'denied' });")
- .arg(variableName)
- .arg(apiCall);
- evaluateJavaScriptSync(&page, js);
- };
-
- // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call
- // from JS
- triggerRequest("readState", "readText()");
- QTRY_COMPARE(evaluateJavaScriptSync(&page, "readState"), finalPermission);
- triggerRequest("writeState", "writeText('foo')");
- QTRY_COMPARE(evaluateJavaScriptSync(&page, "writeState"), finalPermission);
- QCOMPARE(permissionRequestCount, canAccessClipboard ? 0 : 2);
- QVERIFY(!errorState);
-}
-
void tst_QWebEnginePage::contentsSize()
{
m_view->resize(800, 600);
@@ -4160,62 +3837,6 @@ void tst_QWebEnginePage::contentsSize()
QCOMPARE(m_page->contentsSize().height(), 1216);
}
-void tst_QWebEnginePage::localFontAccessPermission_data()
-{
- QTest::addColumn<QWebEnginePermission::State>("policy");
- QTest::addColumn<bool>("ignore");
- QTest::addColumn<bool>("shouldBeEmpty");
-
- QTest::newRow("ignore") << QWebEnginePermission::State::Denied << true << true;
- QTest::newRow("setDeny") << QWebEnginePermission::State::Denied << false << true;
- QTest::newRow("setGrant") << QWebEnginePermission::State::Granted << false << false;
-}
-
-void tst_QWebEnginePage::localFontAccessPermission() {
- QFETCH(QWebEnginePermission::State, policy);
- QFETCH(bool, ignore);
- QFETCH(bool, shouldBeEmpty);
-
- QWebEngineView view;
- QWebEnginePage page(&view);
- page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
- view.setPage(&page);
-
- connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) {
- if (permission.permissionType() != QWebEnginePermission::PermissionType::LocalFontsAccess)
- return;
-
- if (!ignore) {
- if (policy == QWebEnginePermission::State::Granted)
- permission.grant();
- else
- permission.deny();
- }
- });
-
- QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
- page.load(QUrl("qrc:///resources/fontaccess.html"));
- QTRY_COMPARE(spy.size(), 1);
-
- // Font access is only enabled for visible WebContents.
- view.show();
- QVERIFY(QTest::qWaitForWindowExposed(&view));
-
- if (evaluateJavaScriptSync(&page, QStringLiteral("!window.queryLocalFonts")).toBool())
- QSKIP("Local fonts access is not supported.");
-
- // Access to the API requires recent user interaction
- QTest::keyPress(view.focusProxy(), Qt::Key_Space);
- QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("activated")).toBool(), true);
-
- if (ignore) {
- QTRY_COMPARE_NE_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), true, 1000);
- } else {
- QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool() == true, 1000);
- QVERIFY((evaluateJavaScriptSync(&page, QStringLiteral("fonts.length")).toInt() == 0) == shouldBeEmpty);
- }
-}
-
void tst_QWebEnginePage::setLifecycleState()
{
qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState");
diff --git a/tests/auto/widgets/qwebenginepermission/CMakeLists.txt b/tests/auto/widgets/qwebenginepermission/CMakeLists.txt
new file mode 100644
index 000000000..d6092c8c5
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepermission/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(../../util/util.cmake)
+
+qt_internal_add_test(tst_qwebenginepermission
+ SOURCES
+ tst_qwebenginepermission.cpp
+ LIBRARIES
+ Qt::WebEngineCore
+ Qt::WebEngineWidgets
+ Qt::WebEngineCorePrivate
+ Qt::CorePrivate
+ Test::Util
+)
+
+set(tst_qwebenginepermission_resource_files
+ "resources/index.html"
+ "resources/iframe.html"
+ "resources/qt144.png"
+)
+
+qt_internal_add_resource(tst_qwebenginepermission "tst_qwebenginepermission"
+ PREFIX
+ "/"
+ FILES
+ ${tst_qwebenginepermission_resource_files}
+)
diff --git a/tests/auto/widgets/qwebenginepermission/resources/iframe.html b/tests/auto/widgets/qwebenginepermission/resources/iframe.html
new file mode 100644
index 000000000..483800cd6
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepermission/resources/iframe.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ <iframe name="frame" allow="camera; microphone; display-capture; geolocation; local-fonts; clipboard-read; clipboard-write;" src="qrc:///resources/index.html" width="400" height="400"></iframe>
+</body>
+</html>
diff --git a/tests/auto/widgets/qwebenginepermission/resources/index.html b/tests/auto/widgets/qwebenginepermission/resources/index.html
new file mode 100644
index 000000000..6150a2bd9
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepermission/resources/index.html
@@ -0,0 +1,14 @@
+<html>
+<body onclick='onClick()'>
+<script>
+var triggerFunc = undefined;
+var testFunc = undefined;
+var done = false;
+var skipReason = undefined;
+var data = undefined;
+function onClick() {
+ triggerFunc();
+}
+</script>
+</body>
+</html>
diff --git a/tests/auto/widgets/qwebenginepermission/resources/qt144.png b/tests/auto/widgets/qwebenginepermission/resources/qt144.png
new file mode 100644
index 000000000..050b1e066
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepermission/resources/qt144.png
Binary files differ
diff --git a/tests/auto/widgets/qwebenginepermission/tst_qwebenginepermission.cpp b/tests/auto/widgets/qwebenginepermission/tst_qwebenginepermission.cpp
new file mode 100644
index 000000000..bfc70557e
--- /dev/null
+++ b/tests/auto/widgets/qwebenginepermission/tst_qwebenginepermission.cpp
@@ -0,0 +1,727 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <util.h>
+
+#include <QtTest/QtTest>
+#include <QDir>
+#include <QStringLiteral>
+#include <QWebEngineDesktopMediaRequest>
+#include <QWebEngineFrame>
+#include <QWebEnginePage>
+#include <QWebEnginePermission>
+#include <QWebEngineProfile>
+#include <QWebEngineSettings>
+#include <QWebEngineView>
+
+using namespace Qt::StringLiterals;
+
+class tst_QWebEnginePermission : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebEnginePermission();
+ ~tst_QWebEnginePermission();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void triggerFromJavascript_data();
+ void triggerFromJavascript();
+ void preGrant_data();
+ void preGrant();
+ void iframe_data();
+ void iframe();
+
+ void permissionPersistence_data();
+ void permissionPersistence();
+
+ void queryPermission_data();
+ void queryPermission();
+ void listPermissions();
+
+ void clipboardReadWritePermissionInitialState_data();
+ void clipboardReadWritePermissionInitialState();
+ void clipboardReadWritePermission_data();
+ void clipboardReadWritePermission();
+
+private:
+ std::unique_ptr<QWebEngineProfile> m_profile;
+ QString m_profileName;
+};
+
+tst_QWebEnginePermission::tst_QWebEnginePermission()
+ : m_profileName("tst_QWebEnginePermission")
+{
+}
+
+tst_QWebEnginePermission::~tst_QWebEnginePermission()
+{
+}
+
+void tst_QWebEnginePermission::initTestCase()
+{
+}
+
+void tst_QWebEnginePermission::cleanupTestCase()
+{
+}
+
+void tst_QWebEnginePermission::init()
+{
+ m_profile.reset(new QWebEngineProfile("tst_QWebEnginePermission"));
+}
+
+void tst_QWebEnginePermission::cleanup()
+{
+ if (m_profile && m_profile->persistentPermissionsPolicy()
+ == QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk) {
+ QDir dir(m_profile->persistentStoragePath());
+ dir.remove("permissions.json");
+
+ // Set a persistent permission to force the creation of a permission.json
+ // in test cases where it wouldn't be created otherwise
+ m_profile->queryPermission(QUrl("https://google.com"),
+ QWebEnginePermission::PermissionType::Notifications).grant();
+
+ // This will trigger the writing of permissions to disk
+ m_profile.reset();
+
+ // Wait for the new permissions.json to be written to disk before deleting
+ QTRY_VERIFY_WITH_TIMEOUT(dir.exists("permissions.json"), 5000);
+ dir.remove("permissions.json");
+ } else {
+ m_profile.reset();
+ }
+}
+
+static QString MediaAudioCapture_trigger =
+ "navigator.mediaDevices.getUserMedia({ video: false, audio: true }).then(s => { data = s; done = true; })"
+ ".catch(err => { skipReason = err.message; done = true; });"_L1;
+static QString MediaAudioCapture_check =
+ "return data != undefined;"_L1;
+
+static QString MediaVideoCapture_trigger =
+ "navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then(s => { data = s; done = true; })"
+ ".catch(err => { skipReason = err.message; done = true; });"_L1;
+static QString MediaVideoCapture_check =
+ "return data != undefined;"_L1;
+
+static QString MediaAudioVideoCapture_trigger =
+ "navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(s => { data = s; done = true; })"
+ ".catch(err => { skipReason = err.message; done = true; });"_L1;
+static QString MediaAudioVideoCapture_check =
+ "return data != undefined;"_L1;
+
+static QString DesktopVideoCapture_trigger =
+ "navigator.mediaDevices.getDisplayMedia({ video: true, audio: false }).then(s => { data = s; done = true; })"
+ ".catch(err => { skipReason = err.message; done = true; });"_L1;
+static QString DesktopVideoCapture_check =
+ "return data != undefined;"_L1;
+
+static QString DesktopAudioVideoCapture_trigger =
+ "navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }).then(s => { data = s; done = true; })"
+ ".catch(err => { skipReason = err.message; done = true; });"_L1;
+static QString DesktopAudioVideoCapture_check =
+ "return data != undefined;"_L1;
+
+static QString MouseLock_trigger =
+ "document.documentElement.requestPointerLock().then(() => { data = document.pointerLockElement(); done = true; }).catch(() => { done = true; });"_L1;
+static QString MouseLock_check =
+ "var ret = (data != undefined); document.exitPointerLock(); return ret;"_L1;
+
+static QString Notifications_trigger =
+ "Notification.requestPermission().then(p => { data = p; done = true; }).catch(() => { done = true; });"_L1;
+static QString Notifications_check =
+ "return data != undefined && Notification.permission === 'granted';"_L1;
+
+static QString Geolocation_trigger =
+ "success = function(p) { data = p; done = true; };"
+ "failure = function(err) { if (err.code === 2) skipReason = 'Positioning is unavailable'; done = true; };"
+ "navigator.geolocation.getCurrentPosition(success, failure);"_L1;
+static QString Geolocation_check =
+ "return data != undefined;"_L1;
+
+static QString ClipboardReadWrite_trigger =
+ "navigator.clipboard.readText().then(c => { data = c; done = true; }).catch(() => { done = true; });"_L1;
+static QString ClipboardReadWrite_check =
+ "return data != undefined;"_L1;
+
+static QString LocalFontsAccess_trigger =
+ "if (!window.queryLocalFonts) { skipReason = 'Local fonts access is not supported on this system'; done = true; }"
+ "else { window.queryLocalFonts().then(f => { data = f; done = true; }); };"_L1;
+static QString LocalFontsAccess_check =
+ "return data.length != 0;"_L1;
+
+static void commonTestData()
+{
+ QTest::addColumn<QWebEnginePermission::PermissionType>("permissionType");
+ QTest::addColumn<QString>("triggerFunction");
+ QTest::addColumn<QString>("testFunction");
+ QTest::addColumn<QWebEngineProfile::PersistentPermissionsPolicy>("policy");
+
+#define QWebEnginePermissionTestCase(pt) \
+ QTest::newRow(#pt "_AskEveryTime") \
+ << QWebEnginePermission::PermissionType::pt \
+ << pt ## _trigger << pt ## _check \
+ << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime; \
+ QTest::newRow(#pt "_StoreInMemory") \
+ << QWebEnginePermission::PermissionType::pt \
+ << pt ## _trigger << pt ## _check \
+ << QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory; \
+ QTest::newRow(#pt "_StoreOnDisk") \
+ << QWebEnginePermission::PermissionType::pt \
+ << pt ## _trigger << pt ## _check \
+ << QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk;
+
+ QWebEnginePermissionTestCase(MediaAudioCapture);
+
+ // Video capture tests don't work with offscreen
+ if (QGuiApplication::platformName() != QLatin1String("offscreen")) {
+ QWebEnginePermissionTestCase(MediaVideoCapture);
+ QWebEnginePermissionTestCase(MediaAudioVideoCapture);
+ QWebEnginePermissionTestCase(DesktopVideoCapture);
+ QWebEnginePermissionTestCase(DesktopAudioVideoCapture);
+ }
+ // QWebEnginePermissionTestCase(MouseLock); // currently untestable
+ QWebEnginePermissionTestCase(Notifications);
+#ifndef Q_OS_MACOS
+ QWebEnginePermissionTestCase(Geolocation);
+#endif
+ QWebEnginePermissionTestCase(ClipboardReadWrite);
+ QWebEnginePermissionTestCase(LocalFontsAccess);
+
+#undef QWebEnginePermissionTestCase
+}
+
+void tst_QWebEnginePermission::triggerFromJavascript_data()
+{
+ commonTestData();
+}
+
+void tst_QWebEnginePermission::triggerFromJavascript()
+{
+ QFETCH(QWebEnginePermission::PermissionType, permissionType);
+ QFETCH(QString, triggerFunction);
+ QFETCH(QString, testFunction);
+ QFETCH(QWebEngineProfile::PersistentPermissionsPolicy, policy);
+
+ QWebEngineView view;
+ QWebEnginePage page(m_profile.get(), &view);
+ m_profile->setPersistentPermissionsPolicy(policy);
+ view.setPage(&page);
+
+ page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
+ connect(&page, &QWebEnginePage::desktopMediaRequested, &page, [&](const QWebEngineDesktopMediaRequest &request) {
+ request.selectScreen(request.screensModel()->index(0));
+ });
+
+ bool grant = true;
+ QWebEnginePermission permission;
+ connect(&page, &QWebEnginePage::permissionRequested, &page, [&](QWebEnginePermission p) {
+ QCOMPARE(p.permissionType(), permissionType);
+ grant ? p.grant() : p.deny();
+ permission = p;
+ });
+
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ page.load(QUrl("qrc:///resources/index.html"));
+ QTRY_COMPARE(spy.size(), 1);
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ evaluateJavaScriptSync(&page, "triggerFunc = function() {"_L1 + triggerFunction + "}"_L1);
+ evaluateJavaScriptSync(&page, "testFunc = function() {"_L1 + testFunction + "done = true;" + "}"_L1);
+
+ // Access to some pf the APIs requires recent user interaction
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, QPoint{100, 100});
+
+ QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), 5000);
+ if (evaluateJavaScriptSync(&page, QStringLiteral("skipReason")).toBool()) {
+ // Catch expected failures and skip test
+ QSKIP(("Skipping test. Reason: " + evaluateJavaScriptSync(&page, QStringLiteral("skipReason")).toString()).toStdString().c_str());
+ }
+ qWarning() << evaluateJavaScriptSync(&page, QStringLiteral("data"));
+
+ QVERIFY(evaluateJavaScriptSync(&page, QStringLiteral("testFunc()")).toBool());
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Granted);
+
+ // Now reset the permission, and try denying it
+ permission.reset();
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Ask);
+ evaluateJavaScriptSync(&page, "done = false; data = undefined"_L1);
+ grant = false;
+
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, QPoint{100, 100});
+
+ QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), 5000);
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("testFunc()")).toBool(), false);
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Denied);
+}
+
+void tst_QWebEnginePermission::preGrant_data()
+{
+ commonTestData();
+}
+
+void tst_QWebEnginePermission::preGrant()
+{
+ QFETCH(QWebEnginePermission::PermissionType, permissionType);
+ QFETCH(QString, triggerFunction);
+ QFETCH(QString, testFunction);
+ QFETCH(QWebEngineProfile::PersistentPermissionsPolicy, policy);
+
+ QWebEngineView view;
+ QWebEnginePage page(m_profile.get(), &view);
+ m_profile->setPersistentPermissionsPolicy(policy);
+ view.setPage(&page);
+
+ QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished);
+ page.load(QUrl("qrc:///resources/index.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
+ connect(&page, &QWebEnginePage::desktopMediaRequested, &page, [&](const QWebEngineDesktopMediaRequest &request) {
+ request.selectScreen(request.screensModel()->index(0));
+ });
+
+ QWebEnginePermission permission = m_profile->queryPermission(page.url(), permissionType);
+ QVERIFY(permission.state() == QWebEnginePermission::State::Ask);
+ permission.grant();
+
+ evaluateJavaScriptSync(&page, "triggerFunc = function() {"_L1 + triggerFunction + "}"_L1);
+ evaluateJavaScriptSync(&page, "testFunc = function() {"_L1 + testFunction + "done = true;" + "}"_L1);
+
+ QSignalSpy spy(&page, &QWebEnginePage::permissionRequested);
+
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, QPoint{100, 100});
+ QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), 5000);
+ if (evaluateJavaScriptSync(&page, QStringLiteral("skipReason")).toBool()) {
+ // No media devices, or no geolocation plugin
+ QSKIP(("Skipping test. Reason: " + evaluateJavaScriptSync(&page, QStringLiteral("skipReason")).toString()).toStdString().c_str());
+ }
+ QVERIFY(evaluateJavaScriptSync(&page, QStringLiteral("testFunc()")).toBool());
+
+ // The permissionRequested signal must NOT fire
+ QCOMPARE(spy.size(), 0);
+}
+
+void tst_QWebEnginePermission::iframe_data()
+{
+ commonTestData();
+}
+
+void tst_QWebEnginePermission::iframe()
+{
+ QFETCH(QWebEnginePermission::PermissionType, permissionType);
+ QFETCH(QString, triggerFunction);
+ QFETCH(QString, testFunction);
+ QFETCH(QWebEngineProfile::PersistentPermissionsPolicy, policy);
+
+ QWebEngineView view;
+ QWebEnginePage page(m_profile.get(), &view);
+ m_profile->setPersistentPermissionsPolicy(policy);
+ view.setPage(&page);
+
+ page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
+ connect(&page, &QWebEnginePage::desktopMediaRequested, &page, [&](const QWebEngineDesktopMediaRequest &request) {
+ request.selectScreen(request.screensModel()->index(0));
+ });
+
+ bool grant = true;
+ QWebEnginePermission permission;
+ connect(&page, &QWebEnginePage::permissionRequested, &page, [&](QWebEnginePermission p) {
+ grant ? p.grant() : p.deny();
+ permission = p;
+ });
+
+ QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished);
+ page.load(QUrl("qrc:///resources/iframe.html"));
+ QTRY_COMPARE(loadSpy.size(), 1);
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ auto maybeFrame = page.findFrameByName("frame");
+ QVERIFY(maybeFrame);
+ QWebEngineFrame &frame = maybeFrame.value();
+
+ evaluateJavaScriptSync(&frame, "triggerFunc = function() {"_L1 + triggerFunction + "}"_L1);
+ evaluateJavaScriptSync(&frame, "testFunc = function() {"_L1 + testFunction + "done = true;" + "}"_L1);
+
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, QPoint{100, 100});
+
+ QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&frame, QStringLiteral("done")).toBool(), 10000);
+ if (evaluateJavaScriptSync(&frame, QStringLiteral("skipReason")).toBool()) {
+ // Catch expected failures and skip test
+ QSKIP(("Skipping test. Reason: " + evaluateJavaScriptSync(&frame, QStringLiteral("skipReason")).toString()).toStdString().c_str());
+ }
+
+ QVERIFY(evaluateJavaScriptSync(&frame, QStringLiteral("testFunc()")).toBool());
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Granted);
+
+ // Now reset the permission, and try denying it
+ permission.reset();
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Ask);
+ evaluateJavaScriptSync(&frame, "done = false; data = undefined"_L1);
+ grant = false;
+
+ // Only test non-persistent permissions past this point
+ if (QWebEnginePermission::isPersistent(permissionType)
+ && policy != QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime)
+ return;
+
+ // Perform a cross-origin navigation and then go back to check if the permission has been cleared
+ // We don't need a valid URL to trigger the cross-origin logic.
+ evaluateJavaScriptSync(&page, "document.getElementsByName('frame')[0].src = 'http://bad-url.bad-url'"_L1);
+ QTRY_VERIFY_WITH_TIMEOUT(frame.url() != QUrl("qrc:///resources/index.html"_L1), 10000);
+ evaluateJavaScriptSync(&page, "document.getElementsByName('frame')[0].src = 'qrc:///resources/index.html'"_L1);
+ QTRY_VERIFY_WITH_TIMEOUT(frame.url() == QUrl("qrc:///resources/index.html"_L1), 10000);
+
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Ask);
+}
+
+void tst_QWebEnginePermission::permissionPersistence_data()
+{
+ QTest::addColumn<QWebEngineProfile::PersistentPermissionsPolicy>("policy");
+ QTest::addColumn<bool>("granted");
+
+ QTest::newRow("noPersistenceDeny") << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime << false;
+ QTest::newRow("noPersistenceGrant") << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime << true;
+ QTest::newRow("memoryPersistenceDeny") << QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory << false;
+ QTest::newRow("memoryPersistenceGrant") << QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory << true;
+ QTest::newRow("diskPersistenceDeny") << QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk << false;
+ QTest::newRow("diskPersistenceGrant") << QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk << true;
+}
+
+void tst_QWebEnginePermission::permissionPersistence()
+{
+ QFETCH(QWebEngineProfile::PersistentPermissionsPolicy, policy);
+ QFETCH(bool, granted);
+
+ m_profile->setPersistentPermissionsPolicy(policy);
+
+ std::unique_ptr<QWebEnginePage> page(new QWebEnginePage(m_profile.get()));
+ std::unique_ptr<QSignalSpy> loadSpy(new QSignalSpy(page.get(), &QWebEnginePage::loadFinished));
+ QDir storageDir = QDir(m_profile->persistentStoragePath());
+
+ page->load(QUrl("qrc:///resources/index.html"_L1));
+ QTRY_COMPARE(loadSpy->size(), 1);
+
+ QVariant variant = granted ? "granted" : "denied";
+ QVariant defaultVariant = "default";
+
+ QWebEnginePermission permissionObject = m_profile->queryPermission(
+ QUrl("qrc:///resources/index.html"_L1), QWebEnginePermission::PermissionType::Notifications);
+ if (granted)
+ permissionObject.grant();
+ else
+ permissionObject.deny();
+ QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), variant);
+
+ page.reset();
+ m_profile.reset();
+ loadSpy.reset();
+
+ bool expectSame = false;
+ if (policy == QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk) {
+ expectSame = true;
+
+ // File is written asynchronously, wait for it to be created
+ QTRY_COMPARE(storageDir.exists("permissions.json"), true);
+ }
+
+ m_profile.reset(new QWebEngineProfile(m_profileName));
+ m_profile->setPersistentPermissionsPolicy(policy);
+
+ page.reset(new QWebEnginePage(m_profile.get()));
+ loadSpy.reset(new QSignalSpy(page.get(), &QWebEnginePage::loadFinished));
+ page->load(QUrl("qrc:///resources/index.html"_L1));
+ QTRY_COMPARE(loadSpy->size(), 1);
+ QTRY_COMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"),
+ expectSame ? variant : defaultVariant);
+
+ // Re-acquire the permission, since deleting the Profile makes it invalid
+ permissionObject = m_profile->queryPermission(QUrl("qrc:///resources/index.html"_L1), QWebEnginePermission::PermissionType::Notifications);
+ permissionObject.reset();
+ QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), defaultVariant);
+}
+
+void tst_QWebEnginePermission::queryPermission_data()
+{
+ QTest::addColumn<QWebEnginePermission::PermissionType>("permissionType");
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<bool>("expectedValid");
+
+ QTest::newRow("badUrl")
+ << QWebEnginePermission::PermissionType::Notifications << QUrl("//:bad-url"_L1) << false;
+ QTest::newRow("badFeature")
+ << QWebEnginePermission::PermissionType::Unsupported << QUrl("qrc:/resources/index.html"_L1) << false;
+ QTest::newRow("transientFeature")
+ << QWebEnginePermission::PermissionType::MouseLock << QUrl("qrc:/resources/index.html"_L1) << true;
+ QTest::newRow("good")
+ << QWebEnginePermission::PermissionType::Notifications << QUrl("qrc:/resources/index.html"_L1) << true;
+}
+
+void tst_QWebEnginePermission::queryPermission()
+{
+ QFETCH(QWebEnginePermission::PermissionType, permissionType);
+ QFETCH(QUrl, url);
+ QFETCH(bool, expectedValid);
+
+ // In-memory is the default for otr profiles
+ m_profile.reset(new QWebEngineProfile());
+ QVERIFY(m_profile->persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory);
+
+ QWebEnginePermission permission = m_profile->queryPermission(url, permissionType);
+ bool valid = permission.isValid();
+ QCOMPARE(valid, expectedValid);
+ if (!valid)
+ QCOMPARE(permission.state(), QWebEnginePermission::State::Invalid);
+
+ // Verify that we can grant a valid permission, and we can't grant an invalid one...
+ permission.grant();
+ QCOMPARE(permission.state(), valid ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Invalid);
+
+ // ...and that doing so twice doesn't mess up the state...
+ permission.grant();
+ QCOMPARE(permission.state(), valid ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Invalid);
+
+ // ...and that the same thing applies to denying them...
+ permission.deny();
+ QCOMPARE(permission.state(), valid ? QWebEnginePermission::State::Denied : QWebEnginePermission::State::Invalid);
+ permission.deny();
+ QCOMPARE(permission.state(), valid ? QWebEnginePermission::State::Denied : QWebEnginePermission::State::Invalid);
+
+ // ...and that resetting works
+ permission.reset();
+ QCOMPARE(permission.state(), valid ? QWebEnginePermission::State::Ask : QWebEnginePermission::State::Invalid);
+ permission.reset();
+ QCOMPARE(permission.state(), valid ? QWebEnginePermission::State::Ask : QWebEnginePermission::State::Invalid);
+}
+
+void tst_QWebEnginePermission::listPermissions()
+{
+ // In-memory is the default for otr profiles
+ m_profile.reset(new QWebEngineProfile());
+ QVERIFY(m_profile->persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory);
+
+ QUrl commonUrl = QUrl(QStringLiteral("https://www.bing.com/maps"));
+ QWebEnginePermission::PermissionType commonType = QWebEnginePermission::PermissionType::Notifications;
+
+ // First, set several permissions at once
+ m_profile->queryPermission(commonUrl, QWebEnginePermission::PermissionType::Geolocation).deny();
+ m_profile->queryPermission(commonUrl, QWebEnginePermission::PermissionType::Unsupported).grant(); // Invalid
+ m_profile->queryPermission(commonUrl, commonType).grant();
+ m_profile->queryPermission(QUrl(QStringLiteral("https://www.google.com/translate")), commonType).grant();
+
+ QList<QWebEnginePermission> permissionsListAll = m_profile->listAllPermissions();
+ QList<QWebEnginePermission> permissionsListUrl = m_profile->listPermissionsForOrigin(commonUrl);
+ QList<QWebEnginePermission> permissionsListFeature = m_profile->listPermissionsForPermissionType(commonType);
+
+ // Order of returned permissions is not guaranteed, so we must iterate until we find the one we need
+ auto findInList = [](QList<QWebEnginePermission> list, const QUrl &url,
+ QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state)
+ {
+ bool found = false;
+ for (auto &permission : list) {
+ if (permission.origin().adjusted(QUrl::RemovePath) == url.adjusted(QUrl::RemovePath)
+ && permission.permissionType() == permissionType && permission.state() == state) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ };
+
+ // Check full list
+ QVERIFY(permissionsListAll.size() == 3);
+ QVERIFY(findInList(permissionsListAll, commonUrl, QWebEnginePermission::PermissionType::Geolocation, QWebEnginePermission::State::Denied));
+ QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
+ QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("https://www.google.com")), commonType, QWebEnginePermission::State::Granted));
+
+ // Check list filtered by URL
+ QVERIFY(permissionsListUrl.size() == 2);
+ QVERIFY(findInList(permissionsListUrl, commonUrl, QWebEnginePermission::PermissionType::Geolocation, QWebEnginePermission::State::Denied));
+ QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
+
+ // Check list filtered by feature
+ QVERIFY(permissionsListFeature.size() == 2);
+ QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
+ QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("https://www.google.com")), commonType, QWebEnginePermission::State::Granted));
+}
+
+static QString clipboardPermissionQuery(QString variableName, QString permissionName)
+{
+ return QString("var %1; navigator.permissions.query({ name:'%2' }).then((p) => { %1 = p.state; "
+ "});")
+ .arg(variableName)
+ .arg(permissionName);
+}
+
+void tst_QWebEnginePermission::clipboardReadWritePermissionInitialState_data()
+{
+ QTest::addColumn<bool>("canAccessClipboard");
+ QTest::addColumn<bool>("canPaste");
+ QTest::addColumn<QString>("readPermission");
+ QTest::addColumn<QString>("writePermission");
+ QTest::newRow("access and paste should grant both") << true << true << "granted" << "granted";
+ QTest::newRow("paste only should prompt for both") << false << true << "prompt" << "prompt";
+ QTest::newRow("access only should grant for write only")
+ << true << false << "prompt" << "granted";
+ QTest::newRow("no access or paste should prompt for both")
+ << false << false << "prompt" << "prompt";
+}
+
+void tst_QWebEnginePermission::clipboardReadWritePermissionInitialState()
+{
+ QFETCH(bool, canAccessClipboard);
+ QFETCH(bool, canPaste);
+ QFETCH(QString, readPermission);
+ QFETCH(QString, writePermission);
+
+ m_profile->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
+ QWebEngineView view(m_profile.get());
+ QWebEnginePage &page = *view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard,
+ canAccessClipboard);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, canPaste);
+
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ QUrl baseUrl("https://www.example.com/somepage.html");
+ page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
+ QTRY_COMPARE(spy.size(), 1);
+
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), readPermission);
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), writePermission);
+}
+
+void tst_QWebEnginePermission::clipboardReadWritePermission_data()
+{
+ QTest::addColumn<bool>("canAccessClipboard");
+ QTest::addColumn<QWebEnginePermission::State>("initialPolicy");
+ QTest::addColumn<QString>("initialPermission");
+ QTest::addColumn<QString>("finalPermission");
+
+ QTest::newRow("noAccessGrant")
+ << false << QWebEnginePermission::State::Granted << "granted" << "granted";
+ QTest::newRow("noAccessDeny")
+ << false << QWebEnginePermission::State::Denied << "denied" << "denied";
+ QTest::newRow("noAccessAsk")
+ << false << QWebEnginePermission::State::Ask << "prompt" << "granted";
+
+ // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to
+ // true
+ QTest::newRow("accessGrant")
+ << true << QWebEnginePermission::State::Granted << "granted" << "granted";
+ QTest::newRow("accessDeny")
+ << true << QWebEnginePermission::State::Denied << "granted" << "granted";
+ QTest::newRow("accessAsk")
+ << true << QWebEnginePermission::State::Ask << "granted" << "granted";
+}
+
+void tst_QWebEnginePermission::clipboardReadWritePermission()
+{
+ QFETCH(bool, canAccessClipboard);
+ QFETCH(QWebEnginePermission::State, initialPolicy);
+ QFETCH(QString, initialPermission);
+ QFETCH(QString, finalPermission);
+
+ m_profile->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
+ QWebEngineView view(m_profile.get());
+ QWebEnginePage &page = *view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard,
+ canAccessClipboard);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, true);
+
+ QUrl baseUrl("https://www.example.com/somepage.html");
+
+ int permissionRequestCount = 0;
+ bool errorState = false;
+
+ // This should only fire in the noAccessAsk case. The other NoAccess cases will remember the initial permission,
+ // and the Access cases will auto-grant because JavascriptCanPaste and JavascriptCanAccessClipboard are set.
+ connect(&page, &QWebEnginePage::permissionRequested, &page,
+ [&](QWebEnginePermission permission) {
+ if (permission.permissionType() != QWebEnginePermission::PermissionType::ClipboardReadWrite)
+ return;
+ if (permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
+ qWarning() << "Unexpected case. Can't proceed." << permission.origin();
+ errorState = true;
+ return;
+ }
+ permissionRequestCount++;
+ // Deliberately set to the opposite state; we want to force a fail when this triggers
+ if (initialPolicy == QWebEnginePermission::State::Granted)
+ permission.deny();
+ else
+ permission.grant();
+ });
+
+ QWebEnginePermission permissionObject = m_profile->queryPermission(baseUrl, QWebEnginePermission::PermissionType::ClipboardReadWrite);
+ switch (initialPolicy) {
+ case QWebEnginePermission::State::Granted:
+ permissionObject.grant();
+ break;
+ case QWebEnginePermission::State::Denied:
+ permissionObject.deny();
+ break;
+ case QWebEnginePermission::State::Ask:
+ permissionObject.reset();
+ break;
+ case QWebEnginePermission::State::Invalid:
+ break;
+ }
+
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
+ QTRY_COMPARE(spy.size(), 1);
+
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), initialPermission);
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), initialPermission);
+
+ auto triggerRequest = [&page](QString variableName, QString apiCall)
+ {
+ auto js = QString("var %1; navigator.clipboard.%2.then((v) => { %1 = 'granted' }, (v) => { %1 = "
+ "'denied' });")
+ .arg(variableName)
+ .arg(apiCall);
+ evaluateJavaScriptSync(&page, js);
+ };
+
+ // Permission is remembered, and shouldn't trigger a new request when called from JS
+ triggerRequest("readState", "readText()");
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, "readState"), finalPermission);
+ triggerRequest("writeState", "writeText('foo')");
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, "writeState"), finalPermission);
+
+ if (initialPermission != finalPermission) {
+ QCOMPARE(permissionRequestCount, 1);
+ } else {
+ QCOMPARE(permissionRequestCount, 0);
+ }
+
+ QVERIFY(!errorState);
+}
+
+QTEST_MAIN(tst_QWebEnginePermission)
+#include "tst_qwebenginepermission.moc"
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index eefb41863..ff3eeae65 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -58,11 +58,6 @@ private Q_SLOTS:
void changePersistentCookiesPolicy();
void initiator();
void badDeleteOrder();
- void permissionPersistence_data();
- void permissionPersistence();
- void queryPermission_data();
- void queryPermission();
- void listPermissions();
void qtbug_71895(); // this should be the last test
};
@@ -1034,192 +1029,6 @@ void tst_QWebEngineProfile::badDeleteOrder()
delete view;
}
-void tst_QWebEngineProfile::permissionPersistence_data()
-{
- QTest::addColumn<QWebEngineProfile::PersistentPermissionsPolicy>("policy");
- QTest::addColumn<bool>("granted");
-
- QTest::newRow("noPersistenceNotificationsNoGrant") << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime << false;
- QTest::newRow("noPersistenceNotificationsGrant") << QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime << true;
- QTest::newRow("memoryPersistenceNotificationsNoGrant") << QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory << false;
- QTest::newRow("diskPersistenceNotificationsGrant") << QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk << true;
-}
-
-void tst_QWebEngineProfile::permissionPersistence()
-{
- QFETCH(QWebEngineProfile::PersistentPermissionsPolicy, policy);
- QFETCH(bool, granted);
-
- TestServer server;
- QVERIFY(server.start());
-
- std::unique_ptr<QWebEngineProfile> profile(new QWebEngineProfile("tst_persistence"));
- profile->setPersistentPermissionsPolicy(policy);
-
- std::unique_ptr<QWebEnginePage> page(new QWebEnginePage(profile.get()));
- std::unique_ptr<QSignalSpy> loadSpy(new QSignalSpy(page.get(), &QWebEnginePage::loadFinished));
- QDir storageDir = QDir(profile->persistentStoragePath());
-
- // Delete permissions file if it somehow survived on disk
- storageDir.remove("permissions.json");
-
- page->load(server.url("/hedgehog.html"));
- QTRY_COMPARE(loadSpy->size(), 1);
-
- QVariant variant = granted ? "granted" : "denied";
- QVariant defaultVariant = "default";
-
- QWebEnginePermission permissionObject = profile->queryPermission(server.url("/hedgehog.html"), QWebEnginePermission::PermissionType::Notifications);
- if (granted)
- permissionObject.grant();
- else
- permissionObject.deny();
- QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), variant);
-
- page.reset();
- profile.reset();
- loadSpy.reset();
-
- bool expectSame = false;
- if (policy == QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk) {
- expectSame = true;
-
- // File is written asynchronously, wait for it to be created
- QTRY_COMPARE(storageDir.exists("permissions.json"), true);
- }
-
- profile.reset(new QWebEngineProfile("tst_persistence"));
- profile->setPersistentPermissionsPolicy(policy);
-
- page.reset(new QWebEnginePage(profile.get()));
- loadSpy.reset(new QSignalSpy(page.get(), &QWebEnginePage::loadFinished));
- page->load(server.url("/hedgehog.html"));
- QTRY_COMPARE(loadSpy->size(), 1);
- QTRY_COMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"),
- expectSame ? variant : defaultVariant);
-
- // Re-acquire the permission, since deleting the Profile makes it invalid
- permissionObject = profile->queryPermission(server.url("/hedgehog.html"), QWebEnginePermission::PermissionType::Notifications);
- permissionObject.reset();
- QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), defaultVariant);
-
- page.reset();
- profile.reset();
- loadSpy.reset();
-
- if (policy == QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk) {
- // Wait for file to be written to before deleting
- QTest::qWait(1000);
- storageDir.remove("permissions.json");
- }
-
- QTRY_VERIFY(server.stop());
-}
-
-void tst_QWebEngineProfile::queryPermission_data()
-{
- QTest::addColumn<QWebEnginePermission::PermissionType>("permissionType");
- QTest::addColumn<QUrl>("url");
- QTest::addColumn<bool>("expectedValid");
-
- QTest::newRow("badUrl")
- << QWebEnginePermission::PermissionType::Notifications << QUrl(QStringLiteral("//:bad-url")) << false;
- QTest::newRow("badFeature")
- << QWebEnginePermission::PermissionType::Unsupported << QUrl(QStringLiteral("qrc:/resources/permission.html")) << false;
- QTest::newRow("transientFeature")
- << QWebEnginePermission::PermissionType::MouseLock << QUrl(QStringLiteral("qrc:/resources/permission.html")) << true;
- QTest::newRow("good")
- << QWebEnginePermission::PermissionType::Notifications << QUrl(QStringLiteral("qrc:/resources/permission.html")) << true;
-}
-
-void tst_QWebEngineProfile::queryPermission()
-{
- QFETCH(QWebEnginePermission::PermissionType, permissionType);
- QFETCH(QUrl, url);
- QFETCH(bool, expectedValid);
-
- QWebEngineProfile profile;
- // In-memory is the default for otr profiles
- QVERIFY(profile.persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory);
-
- QWebEnginePermission permission = profile.queryPermission(url, permissionType);
- bool valid = permission.isValid();
- QVERIFY(valid == expectedValid);
- if (!valid)
- QVERIFY(permission.state() == QWebEnginePermission::State::Invalid);
-
- // Verify that we can grant a valid permission, and we can't grant an invalid one...
- permission.grant();
- QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Invalid));
-
- // ...and that doing so twice doesn't mess up the state...
- permission.grant();
- QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Granted : QWebEnginePermission::State::Invalid));
-
- // ...and that the same thing applies to denying them...
- permission.deny();
- QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Denied : QWebEnginePermission::State::Invalid));
- permission.deny();
- QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Denied : QWebEnginePermission::State::Invalid));
-
- // ...and that resetting works
- permission.reset();
- QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Ask : QWebEnginePermission::State::Invalid));
- permission.reset();
- QVERIFY(permission.state() == (valid ? QWebEnginePermission::State::Ask : QWebEnginePermission::State::Invalid));
-}
-
-void tst_QWebEngineProfile::listPermissions()
-{
- QWebEngineProfile profile;
- // In-memory is the default for otr profiles
- QVERIFY(profile.persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory);
-
- QUrl commonUrl = QUrl(QStringLiteral("http://www.bing.com/maps"));
- QWebEnginePermission::PermissionType commonType = QWebEnginePermission::PermissionType::Notifications;
-
- // First, set several permissions at once
- profile.queryPermission(commonUrl, QWebEnginePermission::PermissionType::Geolocation).deny();
- profile.queryPermission(commonUrl, QWebEnginePermission::PermissionType::Unsupported).grant(); // Invalid
- profile.queryPermission(commonUrl, commonType).grant();
- profile.queryPermission(QUrl(QStringLiteral("http://www.google.com/translate")), commonType).grant();
-
- QList<QWebEnginePermission> permissionsListAll = profile.listAllPermissions();
- QList<QWebEnginePermission> permissionsListUrl = profile.listPermissionsForOrigin(commonUrl);
- QList<QWebEnginePermission> permissionsListFeature = profile.listPermissionsForPermissionType(commonType);
-
- // Order of returned permissions is not guaranteed, so we must iterate until we find the one we need
- auto findInList = [](QList<QWebEnginePermission> list, const QUrl &url,
- QWebEnginePermission::PermissionType permissionType, QWebEnginePermission::State state)
- {
- bool found = false;
- for (auto &permission : list) {
- if (permission.origin().adjusted(QUrl::RemovePath) == url.adjusted(QUrl::RemovePath)
- && permission.permissionType() == permissionType && permission.state() == state) {
- found = true;
- break;
- }
- }
- return found;
- };
-
- // Check full list
- QVERIFY(permissionsListAll.size() == 3);
- QVERIFY(findInList(permissionsListAll, commonUrl, QWebEnginePermission::PermissionType::Geolocation, QWebEnginePermission::State::Denied));
- QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
- QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("http://www.google.com")), commonType, QWebEnginePermission::State::Granted));
-
- // Check list filtered by URL
- QVERIFY(permissionsListUrl.size() == 2);
- QVERIFY(findInList(permissionsListUrl, commonUrl, QWebEnginePermission::PermissionType::Geolocation, QWebEnginePermission::State::Denied));
- QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
-
- // Check list filtered by feature
- QVERIFY(permissionsListFeature.size() == 2);
- QVERIFY(findInList(permissionsListAll, commonUrl, commonType, QWebEnginePermission::State::Granted));
- QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("http://www.google.com")), commonType, QWebEnginePermission::State::Granted));
-}
-
void tst_QWebEngineProfile::qtbug_71895()
{
QWebEngineView view;
diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
index 1dfa94565..1c2d761fb 100644
--- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
+++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
@@ -300,7 +300,7 @@ void tst_QWebEngineScript::viewSource()
page.scripts().insert(script);
page.load(QUrl("view-source:about:blank"));
QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
- QTRY_COMPARE(spy.size(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 10000);
QCOMPARE(spy.takeFirst().value(0).toBool(), true);
QCOMPARE(evaluateJavaScriptSync(&page, "foo"), QVariant(42));
}
@@ -314,12 +314,12 @@ void tst_QWebEngineScript::scriptModifications()
script.setWorldId(QWebEngineScript::MainWorld);
script.setSourceCode("var foo = \"SUCCESS\";");
page.scripts().insert(script);
+ QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished);
page.setHtml(QStringLiteral("<html><head><script>document.addEventListener(\"DOMContentLoaded\", function() {\
document.body.innerText = foo;});\
</script></head><body></body></html>"));
QVERIFY(page.scripts().count() == 1);
- QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished);
- QVERIFY(spyFinished.wait());
+ QTRY_VERIFY_WITH_TIMEOUT(spyFinished.size(), 10000);
QCOMPARE(evaluateJavaScriptSync(&page, "document.body.innerText"), QVariant::fromValue(QStringLiteral("SUCCESS")));
script.setSourceCode("var foo = \"FAILURE\"");
page.triggerAction(QWebEnginePage::ReloadAndBypassCache);
@@ -403,9 +403,9 @@ void tst_QWebEngineScript::webChannel()
QWebEngineScript script = webChannelScript();
script.setWorldId(worldId);
page.scripts().insert(script);
- page.setHtml(QStringLiteral("<html><body></body></html>"));
QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished);
- QVERIFY(spyFinished.wait());
+ page.setHtml(QStringLiteral("<html><body></body></html>"));
+ QTRY_VERIFY_WITH_TIMEOUT(spyFinished.size(), 10000);
if (reloadFirst) {
// Check that the transport is also reinstalled on navigation
page.triggerAction(QWebEnginePage::Reload);
@@ -572,7 +572,7 @@ void tst_QWebEngineScript::navigation()
QString url3 = QStringLiteral("qrc:/resources/test_iframe_main.html");
page.setUrl(url3);
- QTRY_COMPARE(spyTextChanged.size(), 3);
+ QTRY_COMPARE_WITH_TIMEOUT(spyTextChanged.size(), 3, 10000);
QCOMPARE(testObject.text(), url3);
page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded);
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index 3c677c3e9..92d8fc1eb 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -400,7 +400,7 @@ void tst_QWebEngineView::changePage()
QSignalSpy pageFromLoadSpy(pageFrom.get(), &QWebEnginePage::loadFinished);
QSignalSpy pageFromIconLoadSpy(pageFrom.get(), &QWebEnginePage::iconChanged);
pageFrom->load(urlFrom);
- QTRY_COMPARE(pageFromLoadSpy.size(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(pageFromLoadSpy.size(), 1, 10000);
QCOMPARE(pageFromLoadSpy.last().value(0).toBool(), true);
if (!fromIsNullPage) {
QTRY_COMPARE(pageFromIconLoadSpy.size(), 1);
@@ -1271,7 +1271,7 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem()
webView->setHtml("<html><body>"
" <input id='input1' type='text'/>"
"</body></html>");
- QTRY_COMPARE(loadSpy.size(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 10000);
QTRY_COMPARE(webView->hasFocus(), false);
// Manually trigger focus.
@@ -1404,7 +1404,7 @@ void tst_QWebEngineView::mixLangLocale()
auto sc = connect(view.page(), &QWebEnginePage::renderProcessTerminated, [&] () { terminated = true; });
view.load(QUrl("qrc:///resources/dummy.html"));
- QTRY_VERIFY(terminated || loadSpy.size() == 1);
+ QTRY_VERIFY_WITH_TIMEOUT(terminated || loadSpy.size() == 1, 10000);
QVERIFY2(!terminated,
qPrintable(QString("Locale [%1] terminated: %2, loaded: %3").arg(locale).arg(terminated).arg(loadSpy.size())));
@@ -1622,7 +1622,7 @@ void tst_QWebEngineView::keyboardFocusAfterPopup()
// Trigger QCompleter's popup and select the first suggestion.
QTest::keyClick(QApplication::focusWindow(), Qt::Key_T);
- QTRY_VERIFY(QApplication::activePopupWidget());
+ QTRY_VERIFY_WITH_TIMEOUT(QApplication::activePopupWidget(), 10000);
QTest::keyClick(QApplication::focusWindow(), Qt::Key_Down);
QTest::keyClick(QApplication::focusWindow(), Qt::Key_Enter);
@@ -2071,7 +2071,7 @@ void tst_QWebEngineView::inputContextQueryInput()
view.setHtml("<html><body>"
" <input type='text' id='input1' value='' size='50'/>"
"</body></html>");
- QTRY_COMPARE(loadFinishedSpy.size(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000);
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(testContext.infos.size(), 0);
@@ -2223,7 +2223,7 @@ void tst_QWebEngineView::inputMethods()
view.setHtml("<html><body>"
" <input type='text' id='input1' style='font-family: serif' value='' maxlength='20' size='50'/>"
"</body></html>");
- QTRY_COMPARE(loadFinishedSpy.size(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000);
QVERIFY(QTest::qWaitForWindowExposed(&view));
QPoint textInputCenter = elementCenter(view.page(), "input1");
@@ -2405,7 +2405,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField()
view.setHtml("<html><body>"
" This is a text"
"</body></html>");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCOMPARE(selectionChangedSpy.size(), 0);
@@ -2499,7 +2499,7 @@ void tst_QWebEngineView::hiddenText()
" <input type='text' id='input1' value='QtWebEngine' size='50'/><br>"
" <input type='password' id='password1'/>"
"</body></html>");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
QPoint passwordInputCenter = elementCenter(view.page(), "password1");
QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, passwordInputCenter);
@@ -2526,7 +2526,7 @@ void tst_QWebEngineView::emptyInputMethodEvent()
view.setHtml("<html><body>"
" <input type='text' id='input1' value='QtWebEngine'/>"
"</body></html>");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
QVERIFY(QTest::qWaitForWindowExposed(&view));
evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();");
@@ -2924,7 +2924,7 @@ void tst_QWebEngineView::imeJSInputEvents()
" <div id='input' contenteditable='true' style='border-style: solid;'></div>"
" <pre id='log'></pre>"
"</body></html>");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
QVERIFY(QTest::qWaitForWindowExposed(&view));
evaluateJavaScriptSync(view.page(), "document.getElementById('input').focus()");
@@ -3331,7 +3331,7 @@ void tst_QWebEngineView::mouseLeave()
" <div id='testDiv' style='width: 100%; height: 100%; background-color: green' />"
"</body>"
"</html>");
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
// Make sure the testDiv text is empty.
evaluateJavaScriptSync(view->page(), "document.getElementById('testDiv').innerText = ''");
QTRY_VERIFY(innerText().isEmpty());
@@ -4015,7 +4015,7 @@ void tst_QWebEngineView::longKeyEventText()
view.resize(200, 400);
view.show();
view.setHtml(html);
- QTRY_VERIFY(loadFinishedSpy.size());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
QSignalSpy consoleMessageSpy(&page, &ConsolePage::done);
Qt::Key key(Qt::Key_Shift);
QKeyEvent event(QKeyEvent::KeyPress, key, Qt::NoModifier, QKeySequence(key).toString());
@@ -4035,7 +4035,7 @@ void tst_QWebEngineView::deferredDelete()
QSignalSpy loadFinishedSpy(view.page(), &QWebEnginePage::loadFinished);
view.load(QUrl("chrome://qt"));
view.show();
- QTRY_VERIFY(loadFinishedSpy.size());
+ QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size(), 10000);
// QWebEngineView and WebEngineQuickWidget
QCOMPARE(QApplication::allWidgets().size(), desktopWidget + 2);
}
@@ -4076,7 +4076,7 @@ void tst_QWebEngineView::setCursorOnEmbeddedView()
QVERIFY(QTest::qWaitForWindowActive(&parentWidget));
- QTRY_VERIFY(firstPaintSpy.size());
+ QTRY_VERIFY_WITH_TIMEOUT(firstPaintSpy.size(), 10000);
const QPoint step = QPoint(25, 25);
QPoint cursorPos = view.pos() - step;