summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp42
-rw-r--r--src/webenginewidgets/api/qwebenginepage.h18
-rw-r--r--src/webenginewidgets/api/qwebenginepage_p.h2
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp65
4 files changed, 126 insertions, 1 deletions
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<bool> &resultCallback = QWebEngineCallback<bool>());
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)