MediaDevices should handle changes of iframe allow attribute value
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Feb 2020 10:57:44 +0000 (10:57 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Feb 2020 10:57:44 +0000 (10:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=207112

Reviewed by Eric Carlson.

LayoutTests/imported/w3c:

* web-platform-tests/mediacapture-streams/MediaStream-default-feature-policy.https-expected.txt:
* web-platform-tests/mediacapture-streams/MediaStream-feature-policy-none.https-expected.txt:

Source/WebCore:

MediaDevices was computing whether it could access camera or microphone at creation time.
Since the iframe allow attribute can be modified, we cannot do that.
Instead, we get the feature policy everytime this is needed.

Refactor code to use the newly added routine to check for feature policy.
Update logging to give origin and allow attribute value of the frame that fail the feature policy check.

Test: http/tests/webrtc/enumerateDevicesInFrames.html

* Modules/mediastream/MediaDevices.cpp:
(WebCore::MediaDevices::MediaDevices):
(WebCore::MediaDevices::refreshDevices):
(WebCore::MediaDevices::enumerateDevices):
(WebCore::MediaDevices::listenForDeviceChanges):
* Modules/mediastream/MediaDevices.h:
* Modules/mediastream/UserMediaController.cpp:
(WebCore::UserMediaController::logGetUserMediaDenial):
(WebCore::UserMediaController::logGetDisplayMediaDenial):
(WebCore::UserMediaController::logEnumerateDevicesDenial):
* Modules/mediastream/UserMediaController.h:
* Modules/mediastream/UserMediaRequest.cpp:
(WebCore::UserMediaRequest::start):
* html/FeaturePolicy.cpp:
(WebCore::policyTypeName):
(WebCore::isFeaturePolicyAllowedByDocumentAndAllOwners):
* html/FeaturePolicy.h:
* page/DOMWindow.cpp:
(WebCore::DOMWindow::printErrorMessage const):
* page/DOMWindow.h:

LayoutTests:

* TestExpectations:
* fullscreen/full-screen-enabled-expected.txt:
* fullscreen/full-screen-enabled-prefixed-expected.txt:
* fullscreen/full-screen-iframe-not-allowed-expected.txt:
* fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt:
* fullscreen/full-screen-restrictions-expected.txt:
* http/tests/fullscreen/fullscreen-feature-policy-expected.txt:
* http/tests/media/media-stream/enumerate-devices-iframe-allow-attribute-expected.txt:
* http/tests/media/media-stream/get-display-media-iframe-allow-attribute-expected.txt:
* http/tests/ssl/media-stream/get-user-media-different-host-expected.txt:
* http/tests/ssl/media-stream/get-user-media-nested-expected.txt:
* http/tests/webrtc/enumerateDevicesInFrames-expected.txt: Added.
* http/tests/webrtc/enumerateDevicesInFrames.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@255668 268f45cc-cd09-0410-ab3c-d52691b4dbfc

28 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/fullscreen/full-screen-enabled-expected.txt
LayoutTests/fullscreen/full-screen-enabled-prefixed-expected.txt
LayoutTests/fullscreen/full-screen-iframe-not-allowed-expected.txt
LayoutTests/fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt
LayoutTests/fullscreen/full-screen-restrictions-expected.txt
LayoutTests/http/tests/fullscreen/fullscreen-feature-policy-expected.txt
LayoutTests/http/tests/media/media-stream/enumerate-devices-iframe-allow-attribute-expected.txt
LayoutTests/http/tests/media/media-stream/get-display-media-iframe-allow-attribute-expected.txt
LayoutTests/http/tests/media/media-stream/get-display-media-iframe-allow-attribute.html
LayoutTests/http/tests/ssl/media-stream/get-user-media-different-host-expected.txt
LayoutTests/http/tests/ssl/media-stream/get-user-media-nested-expected.txt
LayoutTests/http/tests/webrtc/enumerateDevicesInFrames-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/webrtc/enumerateDevicesInFrames.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/mediacapture-streams/MediaStream-default-feature-policy.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/mediacapture-streams/MediaStream-feature-policy-none.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/MediaDevices.cpp
Source/WebCore/Modules/mediastream/MediaDevices.h
Source/WebCore/Modules/mediastream/UserMediaController.cpp
Source/WebCore/Modules/mediastream/UserMediaController.h
Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
Source/WebCore/html/FeaturePolicy.cpp
Source/WebCore/html/FeaturePolicy.h
Source/WebCore/page/DOMWindow.cpp
Source/WebCore/page/DOMWindow.h

index b2786de..cbec285 100644 (file)
@@ -1,3 +1,24 @@
+2020-02-04  youenn fablet  <youenn@apple.com>
+
+        MediaDevices should handle changes of iframe allow attribute value
+        https://bugs.webkit.org/show_bug.cgi?id=207112
+
+        Reviewed by Eric Carlson.
+
+        * TestExpectations:
+        * fullscreen/full-screen-enabled-expected.txt:
+        * fullscreen/full-screen-enabled-prefixed-expected.txt:
+        * fullscreen/full-screen-iframe-not-allowed-expected.txt:
+        * fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt:
+        * fullscreen/full-screen-restrictions-expected.txt:
+        * http/tests/fullscreen/fullscreen-feature-policy-expected.txt:
+        * http/tests/media/media-stream/enumerate-devices-iframe-allow-attribute-expected.txt:
+        * http/tests/media/media-stream/get-display-media-iframe-allow-attribute-expected.txt:
+        * http/tests/ssl/media-stream/get-user-media-different-host-expected.txt:
+        * http/tests/ssl/media-stream/get-user-media-nested-expected.txt:
+        * http/tests/webrtc/enumerateDevicesInFrames-expected.txt: Added.
+        * http/tests/webrtc/enumerateDevicesInFrames.html: Added.
+
 2020-02-03  Antti Koivisto  <antti@apple.com>
 
         Accelerated animations freeze on render tree rebuild
index 711b32c..36ef054 100644 (file)
@@ -771,6 +771,8 @@ webkit.org/b/189906 imported/w3c/web-platform-tests/resource-timing/resource_tim
 webkit.org/b/189905 imported/w3c/web-platform-tests/resource-timing/resource_initiator_types.html [ Pass Failure ]
 webkit.org/b/190523 imported/w3c/web-platform-tests/resource-timing/resource_timing_cross_origin_redirect_chain.html [ Pass Failure ]
 
+imported/w3c/web-platform-tests/mediacapture-streams/MediaStream-default-feature-policy.https.html [ DumpJSConsoleLogInStdErr ]
+
 # These fetch tests are flaky or fail.
 imported/w3c/web-platform-tests/fetch/api/redirect/redirect-count.any.html [ Pass Failure ]
 imported/w3c/web-platform-tests/fetch/api/redirect/redirect-count.any.worker.html [ Pass Failure ]
index 012c0d2..92ab65e 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 56: Feature policy 'Fullscreen' check failed for iframe with origin '' and allow attribute 'fullscreen 'none''.
 This tests the fullscreenEnabled property laid out in section 4 of the W3C Full Screen API
 EXPECTED (iframe.contentDocument.webkitFullscreenEnabled == 'true') OK
 EXPECTED (iframe2.contentDocument.webkitFullscreenEnabled == 'false') OK
index 012c0d2..92ab65e 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 56: Feature policy 'Fullscreen' check failed for iframe with origin '' and allow attribute 'fullscreen 'none''.
 This tests the fullscreenEnabled property laid out in section 4 of the W3C Full Screen API
 EXPECTED (iframe.contentDocument.webkitFullscreenEnabled == 'true') OK
 EXPECTED (iframe2.contentDocument.webkitFullscreenEnabled == 'false') OK
index 4026155..c7137c1 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: Feature policy 'Fullscreen' check failed for iframe with origin '' and allow attribute 'fullscreen 'none''.
 Test for bug 56264: Handle entering full screen security restrictions
 
 To test manually, click the "Go full screen" button - the page should not enter full screen mode.
index c6343a5..24256bc 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 1: Feature policy 'Fullscreen' check failed for iframe with origin 'null' and allow attribute ''.
 Test entering full screen security restrictions. An iframe without an allow attribute is still permitted to fullscreen if the request comes from the containing document.
 
 To test manually, press any key - the page should enter full screen mode.
index 84030de..63d44a3 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: Feature policy 'Fullscreen' check failed for iframe with origin '' and allow attribute 'fullscreen 'none''.
 This tests the restrictions to entering full screen mode laid out in section 4.1 of the W3C Full Screen API
 "The context object is not in a document."
 EVENT(webkitfullscreenerror)
index 5f6dd1a..b76cb03 100644 (file)
@@ -1,3 +1,14 @@
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute ''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute 'fullscreen 'none''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://127.0.0.1:8000' and allow attribute 'fullscreen 'none''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute 'fullscreen 'self''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://127.0.0.1:8000' and allow attribute 'fullscreen http://localhost:8000'.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute 'fullscreen 'none''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://127.0.0.1:8000' and allow attribute 'fullscreen 'none''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute 'fullscreen 'self''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute 'fullscreen 'none''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://127.0.0.1:8000' and allow attribute 'fullscreen 'none''.
+CONSOLE MESSAGE: line 9: Feature policy 'Fullscreen' check failed for iframe with origin 'http://localhost:8000' and allow attribute 'fullscreen 'self''.
 PASS iframe with src="http://loc..." should have document.webkitFullscreenEnabled === false.
 PASS iframe with src="../resourc..." should have document.webkitFullscreenEnabled === true.
 PASS iframe with allow="fullscreen", src="http://loc..." should have document.webkitFullscreenEnabled === true.
index 5b7b75e..5f23aa5 100644 (file)
@@ -1,3 +1,5 @@
-CONSOLE MESSAGE: line 4: Trying to call enumerateDevices from a frame without correct 'allow' attribute.
+CONSOLE MESSAGE: line 4: Feature policy 'Camera' check failed for iframe with origin 'http://localhost:8000' and allow attribute ''.
+CONSOLE MESSAGE: line 4: Feature policy 'Microphone' check failed for iframe with origin 'http://localhost:8000' and allow attribute ''.
+CONSOLE MESSAGE: line 4: Not allowed to call enumerateDevices.
       
 PASS: "allow" attribute respected in all iframes
index 9eeb0a7..c8da819 100644 (file)
@@ -1,6 +1,3 @@
-CONSOLE MESSAGE: line 6: Trying to call getDisplayMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 6: Trying to call getDisplayMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 6: Trying to call getDisplayMedia from a frame without correct 'allow' attribute.
      
 
 PASS: <iframe allow=''> got "deny"
index 63974b6..83a4d4f 100644 (file)
@@ -1,4 +1,5 @@
-CONSOLE MESSAGE: line 52: Trying to call getUserMedia from a frame without correct 'allow' attribute.
+CONSOLE MESSAGE: line 52: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:8443' and allow attribute ''.
+CONSOLE MESSAGE: line 52: Not allowed to call getUserMedia.
 Tests that getUserMedia fails when the top level document and iframe do not have the same domain.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
index b408099..d90bfbc 100644 (file)
@@ -1,4 +1,5 @@
-CONSOLE MESSAGE: line 52: Trying to call getUserMedia from a frame without correct 'allow' attribute.
+CONSOLE MESSAGE: line 52: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:8443' and allow attribute ''.
+CONSOLE MESSAGE: line 52: Not allowed to call getUserMedia.
 Tests that getUserMedia fails when the top level document and iframe do not have the same domain.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/LayoutTests/http/tests/webrtc/enumerateDevicesInFrames-expected.txt b/LayoutTests/http/tests/webrtc/enumerateDevicesInFrames-expected.txt
new file mode 100644 (file)
index 0000000..ea66fbc
--- /dev/null
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: line 18: Feature policy 'Camera' check failed for iframe with origin 'http://127.0.0.1:8000' and allow attribute 'microphone:'none'; camera:'none''.
+CONSOLE MESSAGE: line 18: Feature policy 'Microphone' check failed for iframe with origin 'http://127.0.0.1:8000' and allow attribute 'microphone:'none'; camera:'none''.
+CONSOLE MESSAGE: line 18: Not allowed to call enumerateDevices.
+
+
+PASS allow attribute can be updated dynamically 
+
diff --git a/LayoutTests/http/tests/webrtc/enumerateDevicesInFrames.html b/LayoutTests/http/tests/webrtc/enumerateDevicesInFrames.html
new file mode 100644 (file)
index 0000000..ba90363
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function with_iframe(url, allow) {
+    let frame = document.createElement('iframe');
+    frame.src = url;
+    frame.setAttribute('allow', allow);
+    return new Promise(resolve => {
+        frame.onload = () => { resolve(frame); };
+        document.body.appendChild(frame);
+    });
+}
+
+promise_test(async () => {
+    let frame1 = await with_iframe("resources/", "microphone:'none'; camera:'none'");
+    let devices = await frame1.contentWindow.navigator.mediaDevices.enumerateDevices();
+    assert_equals(devices.length, 0);
+
+    frame1.removeAttribute('allow');
+    devices = await frame1.contentWindow.navigator.mediaDevices.enumerateDevices();
+    assert_not_equals(devices.length, 0);
+}, "allow attribute can be updated dynamically");
+</script>
+</body>
index fc53f84..c219c79 100644 (file)
@@ -1,3 +1,13 @@
+2020-02-04  youenn fablet  <youenn@apple.com>
+
+        MediaDevices should handle changes of iframe allow attribute value
+        https://bugs.webkit.org/show_bug.cgi?id=207112
+
+        Reviewed by Eric Carlson.
+
+        * web-platform-tests/mediacapture-streams/MediaStream-default-feature-policy.https-expected.txt:
+        * web-platform-tests/mediacapture-streams/MediaStream-feature-policy-none.https-expected.txt:
+
 2020-02-02  Rob Buis  <rbuis@igalia.com>
 
         Import WPT tests to verify pings do not send a referrer header
index 48d0aaf..5fd00e4 100644 (file)
@@ -1,12 +1,3 @@
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 13: Trying to call getUserMedia from a frame without correct 'allow' attribute.
 
 
 PASS Default "microphone" feature policy ["self"] allows the top-level document. 
index 24c7b72..9624d7f 100644 (file)
@@ -1,31 +1,59 @@
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 37: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 43: Trying to call getUserMedia from a frame without correct 'allow' attribute.
-CONSOLE MESSAGE: line 49: Trying to call getUserMedia from a frame without correct 'allow' attribute.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'camera 'none''.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'camera 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'camera 'none''.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'camera 'none''.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'camera 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone 'none''.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone 'none';camera 'none''.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone 'none';camera 'none''.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone 'none';camera 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'camera *;microphone 'none''.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'camera *;microphone 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'camera *;microphone 'none''.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'camera *;microphone 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'camera 'none';microphone *'.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'camera 'none';microphone *'.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone *; microphone 'none''.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone *; microphone 'none''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'microphone *; camera 'self''.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'microphone *; camera 'self''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone *; camera http:/example.org self'.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Camera' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone *; camera http:/example.org self'.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'microphone *; camera http:/example.org 'self''.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'microphone *; camera http:/example.org 'self''.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 37: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone https://127.0.0.1:9443'.
+CONSOLE MESSAGE: line 37: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Microphone' check failed for iframe with origin 'https://localhost:9443' and allow attribute 'microphone https://127.0.0.1:9443'.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 43: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'microphone https://127.0.0.1:9443'.
+CONSOLE MESSAGE: line 43: Not allowed to call getUserMedia.
+CONSOLE MESSAGE: line 49: Feature policy 'Camera' check failed for iframe with origin 'https://127.0.0.1:9443' and allow attribute 'microphone https://127.0.0.1:9443'.
+CONSOLE MESSAGE: line 49: Not allowed to call getUserMedia.
 
 PASS "camera 'none'" - same origin iframe 
 PASS "camera 'none'" - cross origin iframe 
index f6ffe12..da0e140 100644 (file)
@@ -1,3 +1,40 @@
+2020-02-04  youenn fablet  <youenn@apple.com>
+
+        MediaDevices should handle changes of iframe allow attribute value
+        https://bugs.webkit.org/show_bug.cgi?id=207112
+
+        Reviewed by Eric Carlson.
+
+        MediaDevices was computing whether it could access camera or microphone at creation time.
+        Since the iframe allow attribute can be modified, we cannot do that.
+        Instead, we get the feature policy everytime this is needed.
+
+        Refactor code to use the newly added routine to check for feature policy.
+        Update logging to give origin and allow attribute value of the frame that fail the feature policy check.
+
+        Test: http/tests/webrtc/enumerateDevicesInFrames.html
+
+        * Modules/mediastream/MediaDevices.cpp:
+        (WebCore::MediaDevices::MediaDevices):
+        (WebCore::MediaDevices::refreshDevices):
+        (WebCore::MediaDevices::enumerateDevices):
+        (WebCore::MediaDevices::listenForDeviceChanges):
+        * Modules/mediastream/MediaDevices.h:
+        * Modules/mediastream/UserMediaController.cpp:
+        (WebCore::UserMediaController::logGetUserMediaDenial):
+        (WebCore::UserMediaController::logGetDisplayMediaDenial):
+        (WebCore::UserMediaController::logEnumerateDevicesDenial):
+        * Modules/mediastream/UserMediaController.h:
+        * Modules/mediastream/UserMediaRequest.cpp:
+        (WebCore::UserMediaRequest::start):
+        * html/FeaturePolicy.cpp:
+        (WebCore::policyTypeName):
+        (WebCore::isFeaturePolicyAllowedByDocumentAndAllOwners):
+        * html/FeaturePolicy.h:
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::printErrorMessage const):
+        * page/DOMWindow.h:
+
 2020-02-03  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         [WinCairo][curl][Clang] error: member access into incomplete type 'WebCore::SynchronousLoaderMessageQueue' in ~CurlRequest
index 0c10ba4..24540e0 100644 (file)
@@ -62,11 +62,6 @@ inline MediaDevices::MediaDevices(Document& document)
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Window) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Window), "MediaDevices::DisplayCaptureSurfaceType::Window is not RealtimeMediaSourceSettings::DisplaySurfaceType::Window as expected");
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Application) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Application), "MediaDevices::DisplayCaptureSurfaceType::Application is not RealtimeMediaSourceSettings::DisplaySurfaceType::Application as expected");
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Browser) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Browser), "MediaDevices::DisplayCaptureSurfaceType::Browser is not RealtimeMediaSourceSettings::DisplaySurfaceType::Browser as expected");
-
-    if (auto* controller = UserMediaController::from(document.page())) {
-        m_canAccessCamera = controller->canCallGetUserMedia(document, { UserMediaController::CaptureType::Camera }) == UserMediaController::GetUserMediaAccess::CanCall;
-        m_canAccessMicrophone = controller->canCallGetUserMedia(document, { UserMediaController::CaptureType::Microphone }) == UserMediaController::GetUserMediaAccess::CanCall;
-    }
 }
 
 MediaDevices::~MediaDevices() = default;
@@ -160,13 +155,30 @@ void MediaDevices::getDisplayMedia(const StreamConstraints& constraints, Promise
     request->start();
 }
 
+static inline bool checkCameraAccess(const Document& document)
+{
+    return isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Camera, document, LogFeaturePolicyFailure::No);
+}
+
+static inline bool checkMicrophoneAccess(const Document& document)
+{
+    return isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Microphone, document, LogFeaturePolicyFailure::No);
+}
+
 void MediaDevices::refreshDevices(const Vector<CaptureDevice>& newDevices)
 {
+    auto* document = this->document();
+    if (!document)
+        return;
+
+    bool canAccessCamera = checkCameraAccess(*document);
+    bool canAccessMicrophone = checkMicrophoneAccess(*document);
+
     Vector<Ref<MediaDeviceInfo>> devices;
     for (auto& newDevice : newDevices) {
-        if (!m_canAccessMicrophone && newDevice.type() == CaptureDevice::DeviceType::Microphone)
+        if (!canAccessMicrophone && newDevice.type() == CaptureDevice::DeviceType::Microphone)
             continue;
-        if (!m_canAccessCamera && newDevice.type() == CaptureDevice::DeviceType::Camera)
+        if (!canAccessCamera && newDevice.type() == CaptureDevice::DeviceType::Camera)
             continue;
 
         auto index = m_devices.findMatching([&newDevice](auto& oldDevice) {
@@ -194,8 +206,9 @@ void MediaDevices::enumerateDevices(EnumerateDevicesPromise&& promise)
         promise.resolve({ });
         return;
     }
-    if (!m_canAccessCamera && !m_canAccessMicrophone) {
-        controller->logGetUserMediaDenial(*document, UserMediaController::GetUserMediaAccess::BlockedByFeaturePolicy, UserMediaController::BlockedCaller::EnumerateDevices);
+
+    if (!checkCameraAccess(*document) && !checkMicrophoneAccess(*document)) {
+        controller->logEnumerateDevicesDenial(*document);
         promise.resolve({ });
         return;
     }
@@ -247,16 +260,19 @@ const char* MediaDevices::activeDOMObjectName() const
 
 void MediaDevices::listenForDeviceChanges()
 {
-    if (m_listeningForDeviceChanges || (!m_canAccessCamera && !m_canAccessMicrophone))
-        return;
-
-    m_listeningForDeviceChanges = true;
-
     auto* document = this->document();
     auto* controller = document ? UserMediaController::from(document->page()) : nullptr;
     if (!controller)
         return;
 
+    bool canAccessCamera = isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Camera, *document, LogFeaturePolicyFailure::No);
+    bool canAccessMicrophone = isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Microphone, *document, LogFeaturePolicyFailure::No);
+
+    if (m_listeningForDeviceChanges || (!canAccessCamera && !canAccessMicrophone))
+        return;
+
+    m_listeningForDeviceChanges = true;
+
     m_deviceChangeToken = controller->addDeviceChangeObserver([weakThis = makeWeakPtr(*this), this]() {
         if (!weakThis || isContextStopped() || m_scheduledEventTimer.isActive())
             return;
index 79631cc..4337b2e 100644 (file)
@@ -121,8 +121,6 @@ private:
     bool m_listeningForDeviceChanges { false };
 
     Vector<Ref<MediaDeviceInfo>> m_devices;
-    bool m_canAccessCamera { false };
-    bool m_canAccessMicrophone { false };
 
     OptionSet<GestureAllowedRequest> m_requestTypesForCurrentGesture;
     WeakPtr<UserGestureToken> m_currentGestureToken;
index 40d5a6e..eb3cb2f 100644 (file)
 
 #if ENABLE(MEDIA_STREAM)
 
-#include "CustomHeaderFields.h"
 #include "DOMWindow.h"
 #include "Document.h"
-#include "DocumentLoader.h"
 #include "Frame.h"
 #include "HTMLIFrameElement.h"
-#include "HTMLParserIdioms.h"
-#include "LegacySchemeRegistry.h"
-#include "Settings.h"
 #include "UserMediaRequest.h"
 
 namespace WebCore {
@@ -61,84 +56,25 @@ void provideUserMediaTo(Page* page, UserMediaClient* client)
     UserMediaController::provideTo(page, UserMediaController::supplementName(), makeUnique<UserMediaController>(client));
 }
 
-static inline bool isAllowedByFeaturePolicy(const FeaturePolicy& featurePolicy, const SecurityOriginData& origin, OptionSet<UserMediaController::CaptureType> types)
+void UserMediaController::logGetUserMediaDenial(Document& document)
 {
-    if ((types & UserMediaController::CaptureType::Camera) && !featurePolicy.allows(FeaturePolicy::Type::Camera, origin))
-        return false;
-
-    if ((types & UserMediaController::CaptureType::Microphone) && !featurePolicy.allows(FeaturePolicy::Type::Microphone, origin))
-        return false;
-
-    if ((types & UserMediaController::CaptureType::Display) && !featurePolicy.allows(FeaturePolicy::Type::DisplayCapture, origin))
-        return false;
-
-    return true;
-}
-
-static UserMediaController::GetUserMediaAccess isAllowedToUse(const Document& document, const Document& topDocument, OptionSet<UserMediaController::CaptureType> types)
-{
-    if (&document == &topDocument)
-        return UserMediaController::GetUserMediaAccess::CanCall;
-
-    auto* parentDocument = document.parentDocument();
-    if (!parentDocument)
-        return UserMediaController::GetUserMediaAccess::BlockedByParent;
-
-    auto* element = document.ownerElement();
-    ASSERT(element);
-    if (!element || !is<HTMLIFrameElement>(*element))
-        return UserMediaController::GetUserMediaAccess::BlockedByParent;
-
-    auto& featurePolicy = downcast<HTMLIFrameElement>(*element).featurePolicy();
-    if (isAllowedByFeaturePolicy(featurePolicy, document.securityOrigin().data(), types))
-        return UserMediaController::GetUserMediaAccess::CanCall;
-
-    return UserMediaController::GetUserMediaAccess::BlockedByFeaturePolicy;
+    if (auto* window = document.domWindow())
+        window->printErrorMessage(makeString("Not allowed to call getUserMedia."));
 }
 
-UserMediaController::GetUserMediaAccess UserMediaController::canCallGetUserMedia(const Document& document, OptionSet<UserMediaController::CaptureType> types) const
+void UserMediaController::logGetDisplayMediaDenial(Document& document)
 {
-    ASSERT(!types.isEmpty());
-
-    auto& topDocument = document.topDocument();
-    if (&document != &topDocument) {
-        for (auto* ancestorDocument = &document; ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) {
-            auto status = isAllowedToUse(*ancestorDocument, topDocument, types);
-            if (status != GetUserMediaAccess::CanCall)
-                return status;
-        }
-    }
-
-    return GetUserMediaAccess::CanCall;
+    if (auto* window = document.domWindow())
+        window->printErrorMessage(makeString("Not allowed to call getDisplayMedia."));
 }
 
-void UserMediaController::logGetUserMediaDenial(Document& document, GetUserMediaAccess access, BlockedCaller caller)
+void UserMediaController::logEnumerateDevicesDenial(Document& document)
 {
-    auto& domWindow = *document.domWindow();
-    const char* callerName;
-
-    switch (caller) {
-    case BlockedCaller::GetUserMedia:
-        callerName = "getUserMedia";
-        break;
-    case BlockedCaller::GetDisplayMedia:
-        callerName = "getDisplayMedia";
-        break;
-    case BlockedCaller::EnumerateDevices:
-        callerName = "enumerateDevices";
-        break;
-    }
-
-    switch (access) {
-    case UserMediaController::GetUserMediaAccess::BlockedByParent:
-        domWindow.printErrorMessage(makeString("The top-level frame has prevented a document with a different security origin from calling ", callerName, "."));
-        break;
-    case GetUserMediaAccess::BlockedByFeaturePolicy:
-        domWindow.printErrorMessage(makeString("Trying to call ", callerName, " from a frame without correct 'allow' attribute."));
-        break;
-    case UserMediaController::GetUserMediaAccess::CanCall:
-        break;
-    }
+    // We redo the check to print to the console log.
+    isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Camera, document, LogFeaturePolicyFailure::Yes);
+    isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Microphone, document, LogFeaturePolicyFailure::Yes);
+    if (auto* window = document.domWindow())
+        window->printErrorMessage(makeString("Not allowed to call enumerateDevices."));
 }
 
 } // namespace WebCore
index bd2fcf4..751ec1e 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "FeaturePolicy.h"
 #include "Page.h"
 #include "UserMediaClient.h"
 #include <wtf/CompletionHandler.h>
@@ -51,24 +52,9 @@ public:
     UserMediaClient::DeviceChangeObserverToken addDeviceChangeObserver(WTF::Function<void()>&&);
     void removeDeviceChangeObserver(UserMediaClient::DeviceChangeObserverToken);
 
-    enum class GetUserMediaAccess {
-        CanCall,
-        BlockedByParent,
-        BlockedByFeaturePolicy,
-    };
-    enum class CaptureType {
-        Microphone = 1 << 0,
-        Camera = 1 << 1,
-        Display = 1 << 3
-    };
-    GetUserMediaAccess canCallGetUserMedia(const Document&, OptionSet<CaptureType>) const;
-
-    enum class BlockedCaller {
-        GetUserMedia,
-        GetDisplayMedia,
-        EnumerateDevices,
-    };
-    void logGetUserMediaDenial(Document&, GetUserMediaAccess, BlockedCaller);
+    void logGetUserMediaDenial(Document&);
+    void logGetDisplayMediaDenial(Document&);
+    void logEnumerateDevicesDenial(Document&);
 
     WEBCORE_EXPORT static const char* supplementName();
     static UserMediaController* from(Page* page) { return static_cast<UserMediaController*>(Supplement<Page>::from(page, supplementName())); }
index e68c59f..df2a5e4 100644 (file)
@@ -198,23 +198,26 @@ void UserMediaRequest::start()
     // 6.10 Permission Failure: Reject p with a new DOMException object whose name attribute has
     //      the value NotAllowedError.
 
-    OptionSet<UserMediaController::CaptureType> types;
-    UserMediaController::BlockedCaller caller;
-    if (m_request.type == MediaStreamRequest::Type::DisplayMedia) {
-        types.add(UserMediaController::CaptureType::Display);
-        caller = UserMediaController::BlockedCaller::GetDisplayMedia;
-    } else {
-        if (m_request.audioConstraints.isValid)
-            types.add(UserMediaController::CaptureType::Microphone);
-        if (m_request.videoConstraints.isValid)
-            types.add(UserMediaController::CaptureType::Camera);
-        caller = UserMediaController::BlockedCaller::GetUserMedia;
-    }
-    auto access = controller->canCallGetUserMedia(document, types);
-    if (access != UserMediaController::GetUserMediaAccess::CanCall) {
-        deny(MediaAccessDenialReason::PermissionDenied);
-        controller->logGetUserMediaDenial(document, access, caller);
-        return;
+    switch (m_request.type) {
+    case MediaStreamRequest::Type::DisplayMedia:
+        if (!isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::DisplayCapture, document)) {
+            deny(MediaAccessDenialReason::PermissionDenied);
+            controller->logGetDisplayMediaDenial(document);
+            return;
+        }
+        break;
+    case MediaStreamRequest::Type::UserMedia:
+        if (m_request.audioConstraints.isValid && !isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Microphone, document)) {
+            deny(MediaAccessDenialReason::PermissionDenied);
+            controller->logGetUserMediaDenial(document);
+            return;
+        }
+        if (m_request.videoConstraints.isValid && !isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::Camera, document)) {
+            deny(MediaAccessDenialReason::PermissionDenied);
+            controller->logGetUserMediaDenial(document);
+            return;
+        }
+        break;
     }
 
     PlatformMediaSessionManager::sharedManager().prepareToSendUserMediaPermissionRequest();
index d4771f5..366b154 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "FeaturePolicy.h"
 
+#include "DOMWindow.h"
 #include "Document.h"
 #include "HTMLIFrameElement.h"
 #include "HTMLNames.h"
@@ -36,19 +37,45 @@ namespace WebCore {
 
 using namespace HTMLNames;
 
-bool isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type type, const Document& document)
+static const char* policyTypeName(FeaturePolicy::Type type)
+{
+    switch (type) {
+    case FeaturePolicy::Type::Camera:
+        return "Camera";
+    case FeaturePolicy::Type::Microphone:
+        return "Microphone";
+    case FeaturePolicy::Type::DisplayCapture:
+        return "DisplayCapture";
+    case FeaturePolicy::Type::SyncXHR:
+        return "SyncXHR";
+    case FeaturePolicy::Type::Fullscreen:
+        return "Fullscreen";
+    }
+    ASSERT_NOT_REACHED();
+    return "";
+}
+
+bool isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type type, const Document& document, LogFeaturePolicyFailure logFailure)
 {
     auto& topDocument = document.topDocument();
     auto* ancestorDocument = &document;
     while (ancestorDocument != &topDocument) {
-        if (!ancestorDocument)
+        if (!ancestorDocument) {
+            if (logFailure == LogFeaturePolicyFailure::Yes && document.domWindow())
+                document.domWindow()->printErrorMessage(makeString("Feature policy '", policyTypeName(type), "' check failed."));
             return false;
+        }
 
         auto* ownerElement = ancestorDocument->ownerElement();
         if (is<HTMLIFrameElement>(ownerElement)) {
             const auto& featurePolicy = downcast<HTMLIFrameElement>(ownerElement)->featurePolicy();
-            if (!featurePolicy.allows(type, ancestorDocument->securityOrigin().data()))
+            if (!featurePolicy.allows(type, ancestorDocument->securityOrigin().data())) {
+                if (logFailure == LogFeaturePolicyFailure::Yes && document.domWindow()) {
+                    auto& allowValue = downcast<HTMLIFrameElement>(ownerElement)->attributeWithoutSynchronization(HTMLNames::allowAttr);
+                    document.domWindow()->printErrorMessage(makeString("Feature policy '", policyTypeName(type), "' check failed for iframe with origin '", document.securityOrigin().toString(), "' and allow attribute '", allowValue, "'."));
+                }
                 return false;
+            }
         }
 
         ancestorDocument = ancestorDocument->parentDocument();
index 654fb83..0151940 100644 (file)
@@ -55,6 +55,7 @@ private:
     AllowRule m_fullscreenRule;
 };
 
-extern bool isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type, const Document&);
+enum class LogFeaturePolicyFailure { No, Yes };
+extern bool isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type, const Document&, LogFeaturePolicyFailure = LogFeaturePolicyFailure::Yes);
 
 } // namespace WebCore
index 0aa80cb..6b70ec3 100644 (file)
@@ -2368,7 +2368,7 @@ void DOMWindow::setLocation(DOMWindow& activeWindow, const URL& completedURL, Se
         lockHistory, lockBackForwardList);
 }
 
-void DOMWindow::printErrorMessage(const String& message)
+void DOMWindow::printErrorMessage(const String& message) const
 {
     if (message.isEmpty())
         return;
index f7e0728..06839a2 100644 (file)
@@ -266,7 +266,7 @@ public:
 
     PageConsoleClient* console() const;
 
-    void printErrorMessage(const String&);
+    void printErrorMessage(const String&) const;
 
     String crossDomainAccessErrorMessage(const DOMWindow& activeWindow, IncludeTargetOrigin);