From 6fca3ec615102f9ce2e1b3330406ef1d654d2474 Mon Sep 17 00:00:00 2001 From: Pierre Rossi Date: Tue, 18 Mar 2014 15:29:22 +0100 Subject: WebRTC Widgets API Simply reuse the existing feature request approach that was used for geolocation and notifications in QtWebKit. Change-Id: I8fec4f4e9e81b491163912fadb4ce17d343864dd Reviewed-by: Zeno Albisser --- src/webenginewidgets/api/qwebenginepage.cpp | 42 ++++++++++++++ src/webenginewidgets/api/qwebenginepage.h | 18 ++++++ src/webenginewidgets/api/qwebenginepage_p.h | 2 +- .../widgets/qwebenginepage/tst_qwebenginepage.cpp | 65 ++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index ec0c0224d..0006cbd42 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -312,6 +312,21 @@ void QWebEnginePagePrivate::authenticationRequired(const QUrl &requestUrl, const *outPassword = networkAuth.password(); } +void QWebEnginePagePrivate::runMediaAccessPermissionRequest(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags requestFlags) +{ + Q_Q(QWebEnginePage); + QWebEnginePage::Feature requestedFeature; + if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture) && requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture)) + requestedFeature = QWebEnginePage::MediaAudioVideoDevices; + else if (requestFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture)) + requestedFeature = QWebEnginePage::MediaAudioDevices; + else if (requestFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture)) + requestedFeature = QWebEnginePage::MediaVideoDevices; + else + return; + Q_EMIT q->featurePermissionRequested(securityOrigin, requestedFeature); +} + void QWebEnginePagePrivate::updateAction(QWebEnginePage::WebAction action) const { #ifdef QT_NO_ACTION @@ -680,6 +695,33 @@ QMenu *QWebEnginePage::createStandardContextMenu() return menu; } +void QWebEnginePage::setFeaturePermission(const QUrl &securityOrigin, QWebEnginePage::Feature feature, QWebEnginePage::PermissionPolicy policy) +{ + Q_D(QWebEnginePage); + WebContentsAdapterClient::MediaRequestFlags flags = WebContentsAdapterClient::MediaNone; + switch (feature) { + case MediaAudioVideoDevices: + case MediaAudioDevices: + case MediaVideoDevices: + if (policy != PermissionUnknown) { + if (policy == PermissionDeniedByUser) + flags = WebContentsAdapterClient::MediaNone; + else { + if (feature == MediaAudioDevices) + flags = WebContentsAdapterClient::MediaAudioCapture; + else if (feature == MediaVideoDevices) + flags = WebContentsAdapterClient::MediaVideoCapture; + else + flags = WebContentsAdapterClient::MediaRequestFlags(WebContentsAdapterClient::MediaVideoCapture | WebContentsAdapterClient::MediaAudioCapture); + } + d->adapter->grantMediaAccessPermission(securityOrigin, flags); + } + break; + default: + break; + } +} + static inline QWebEnginePage::FileSelectionMode toPublic(WebContentsAdapterClient::FileChooserMode mode) { // Should the underlying values change, we'll need a switch here. diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 4e0eaf27f..9ef6e6840 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -111,6 +111,19 @@ public: WebDialog }; + enum PermissionPolicy { + PermissionUnknown, + PermissionGrantedByUser, + PermissionDeniedByUser + }; + + enum Feature { + Notifications, + Geolocation, + MediaAudioDevices, + MediaVideoDevices, + MediaAudioVideoDevices + }; // Ex-QWebFrame enum enum FileSelectionMode { @@ -144,6 +157,8 @@ public: void findText(const QString &subString, FindFlags options = 0, const QWebEngineCallback &resultCallback = QWebEngineCallback()); QMenu *createStandardContextMenu(); + void setFeaturePermission(const QUrl &securityOrigin, Feature feature, PermissionPolicy policy); + // Ex-QWebFrame methods void load(const QUrl &url); void setHtml(const QString &html, const QUrl &baseUrl = QUrl()); @@ -172,6 +187,9 @@ Q_SIGNALS: void geometryChangeRequested(const QRect& geom); void windowCloseRequested(); + void featurePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature); + void featurePermissionRequestCanceled(const QUrl &securityOrigin, QWebEnginePage::Feature feature); + void authenticationRequired(const QUrl &requestUrl, QAuthenticator *authenticator); void proxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *authenticator, const QString &proxyHost); diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index 0c4d3b850..827840c7b 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -136,7 +136,7 @@ public: virtual void passOnFocus(bool reverse) Q_DECL_OVERRIDE { Q_UNUSED(reverse); }; virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) Q_DECL_OVERRIDE; virtual void authenticationRequired(const QUrl &requestUrl, const QString &realm, bool isProxy, const QString &challengingHost, QString *outUser, QString *outPassword) Q_DECL_OVERRIDE; - virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) Q_DECL_OVERRIDE {}; + virtual void runMediaAccessPermissionRequest(const QUrl &securityOrigin, MediaRequestFlags requestFlags) Q_DECL_OVERRIDE; void updateAction(QWebEnginePage::WebAction) const; void updateNavigationActions(); diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index c22ea0051..75f8bf5fb 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -153,6 +153,7 @@ private Q_SLOTS: void userAgentNewlineStripping(); void undoActionHaveCustomText(); void renderWidgetHostViewNotShowTopLevel(); + void getUserMediaRequest(); void viewModes(); @@ -3589,6 +3590,70 @@ void tst_QWebEnginePage::renderWidgetHostViewNotShowTopLevel() QCOMPARE(widget->isVisible(), false); } +class GetUserMediaTestPage : public QWebEnginePage { +Q_OBJECT + +public: + GetUserMediaTestPage() + : m_gotRequest(false) + { + connect(this, &QWebEnginePage::featurePermissionRequested, this, &GetUserMediaTestPage::onFeaturePermissionRequested); + } + + void rejectPendingRequest() + { + setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionDeniedByUser); + m_gotRequest = false; + } + void acceptPendingRequest() + { + setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionGrantedByUser); + m_gotRequest = false; + } + + bool gotFeatureRequest(QWebEnginePage::Feature feature) + { + return m_gotRequest && m_requestedFeature == feature; + } + +private Q_SLOTS: + void onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature) + { + m_requestedFeature = feature; + m_requestSecurityOrigin = securityOrigin; + m_gotRequest = true; + } + +private: + bool m_gotRequest; + QWebEnginePage::Feature m_requestedFeature; + QUrl m_requestSecurityOrigin; + +}; + + +void tst_QWebEnginePage::getUserMediaRequest() +{ + GetUserMediaTestPage *page = new GetUserMediaTestPage(); + + // We need to load content from a resource in order for the securityOrigin to be valid. + page->load(QUrl("qrc:///resources/content.html")); + + QVERIFY(evaluateJavaScriptSync(page, QStringLiteral("!!navigator.webkitGetUserMedia")).toBool()); + evaluateJavaScriptSync(page, QStringLiteral("navigator.webkitGetUserMedia({audio: true}, function() {}, function(){})")); + QTRY_VERIFY_WITH_TIMEOUT(page->gotFeatureRequest(QWebEnginePage::MediaAudioDevices), 100); + // Might end up failing due to the lack of physical media devices deeper in the content layer, so the JS callback is not guaranteed to be called, + // but at least we go through that code path, potentially uncovering failing assertions. + page->acceptPendingRequest(); + + page->runJavaScript(QStringLiteral("errorCallbackCalled = false;")); + evaluateJavaScriptSync(page, QStringLiteral("navigator.webkitGetUserMedia({audio: true, video: true}, function() {}, function(){errorCallbackCalled = true;})")); + QTRY_VERIFY_WITH_TIMEOUT(page->gotFeatureRequest(QWebEnginePage::MediaAudioVideoDevices), 100); + page->rejectPendingRequest(); // Should always end up calling the error callback in JS. + QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(page, QStringLiteral("errorCallbackCalled;")).toBool(), 100); + delete page; +} + void tst_QWebEnginePage::openWindowDefaultSize() { #if !defined(QWEBENGINEPAGE_SETTINGS) -- cgit v1.2.3