diff options
-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 55ac939ad..842b0b106 100644 --- a/src/core/api/qwebengineprofile.cpp +++ b/src/core/api/qwebengineprofile.cpp @@ -1015,7 +1015,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 b6357ce1c..142ad9aff 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> @@ -186,9 +187,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 6aebc18a1..6eb58515c 100644 --- a/src/webenginequick/api/qquickwebengineprofile.cpp +++ b/src/webenginequick/api/qquickwebengineprofile.cpp @@ -1223,7 +1223,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); |