diff options
| author | Kaloyan Chehlarski <kaloyan.chehlarski@qt.io> | 2025-03-19 10:48:31 +0100 |
|---|---|---|
| committer | Kaloyan Chehlarski <kaloyan.chehlarski@qt.io> | 2025-08-20 15:06:23 +0200 |
| commit | bbbe5014e51d2b2c7abce1e63cc971713f9b2540 (patch) | |
| tree | c29ac36122e0e7528e0cebb80adcc0a6776dd83d | |
| parent | 4dfc6e4598310641b94a9c16764d2561883f1046 (diff) | |
Associate permissions with frames
Previously, permisions were associated with the originating WebContents,
as the PermissionManager API uses those in its function calls. This is
insufficient, however, as it results in iframes not receiving the
correct permissions (because they were instead granted to the root
frame instead.
This change modifies the internals of PermissionManagerQt to use
GlobalRenderFrameHostTokens, and modifies relevant functions
across the codebase to pass those around instead of WebContents.
Media and mouse lock permissions now also query the permission manager
before issuing a request, which avoids some cases of the same request
being sent multiple times in a row.
As a side effect, the behavior of non-persistent permissions
(as well as the behavior of ALL permissions when running in
AskEveryTime mode) is standardized so that a permission is remembered
as long as the associated frame is alive.
Fixes: QTBUG-134637
Fixes: QTBUG-135787
Pick-to: 6.10 6.9
Change-Id: I650e3328ef3830d06206acafc3305566d3a10d86
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
| -rw-r--r-- | src/core/api/qwebenginepage.cpp | 79 | ||||
| -rw-r--r-- | src/core/api/qwebenginepage_p.h | 6 | ||||
| -rw-r--r-- | src/core/api/qwebenginepermission.cpp | 39 | ||||
| -rw-r--r-- | src/core/api/qwebenginepermission_p.h | 6 | ||||
| -rw-r--r-- | src/core/api/qwebengineprofile.cpp | 2 | ||||
| -rw-r--r-- | src/core/media_capture_devices_dispatcher.cpp | 30 | ||||
| -rw-r--r-- | src/core/permission_manager_qt.cpp | 435 | ||||
| -rw-r--r-- | src/core/permission_manager_qt.h | 37 | ||||
| -rw-r--r-- | src/core/profile_adapter.cpp | 24 | ||||
| -rw-r--r-- | src/core/profile_adapter.h | 5 | ||||
| -rw-r--r-- | src/core/web_contents_adapter.cpp | 158 | ||||
| -rw-r--r-- | src/core/web_contents_adapter.h | 12 | ||||
| -rw-r--r-- | src/core/web_contents_adapter_client.h | 7 | ||||
| -rw-r--r-- | src/core/web_contents_delegate_qt.cpp | 48 | ||||
| -rw-r--r-- | src/core/web_contents_delegate_qt.h | 3 | ||||
| -rw-r--r-- | src/webenginequick/api/qquickwebengineprofile.cpp | 2 | ||||
| -rw-r--r-- | src/webenginequick/api/qquickwebengineview.cpp | 82 | ||||
| -rw-r--r-- | src/webenginequick/api/qquickwebengineview_p_p.h | 6 |
18 files changed, 554 insertions, 427 deletions
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/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); |
