Handle failure to extend sandbox gracefully
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 May 2018 20:13:34 +0000 (20:13 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 May 2018 20:13:34 +0000 (20:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185779
<rdar://problem/40316349>

Reviewed by Brent Fulgham.

Source/WebCore:

Test: TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm

* Modules/mediastream/UserMediaRequest.cpp:
(WebCore::UserMediaRequest::deny): Include the error string in the promise rejection.
* Modules/mediastream/UserMediaRequest.h:

Source/WebKit:

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _denyNextUserMediaRequest]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:

* UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
(WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted): Don't append
the request to m_grantedRequests if it failed.
(WebKit::UserMediaPermissionRequestManagerProxy::grantAccess): Deny request if willCreateMediaStream
fails.
* UIProcess/UserMediaPermissionRequestManagerProxy.h:

* UIProcess/UserMediaProcessManager.cpp:
(WebKit::UserMediaProcessManager::willCreateMediaStream): Don't try to extend sandbox if
we fail to allocate all necessary handles.
* UIProcess/UserMediaProcessManager.h:
(WebKit::UserMediaProcessManager::denyNextUserMediaRequest): New, for testing.

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm: Added.
(-[SimulateFailedSandboxMessageHandler userContentController:didReceiveScriptMessage:]):
(-[SimulateFailedSandboxUIDelegate _webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:]):
(-[SimulateFailedSandboxUIDelegate _webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:]):
(MediaCaptureSimulateFailedSandbox::SetUp):
(MediaCaptureSimulateFailedSandbox::loadTestAndWaitForMessage):
(TEST_F):
* TestWebKitAPI/Tests/WebKitCocoa/disableGetUserMedia.html:

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

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
Source/WebCore/Modules/mediastream/UserMediaRequest.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp
Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h
Source/WebKit/UIProcess/UserMediaProcessManager.cpp
Source/WebKit/UIProcess/UserMediaProcessManager.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitCocoa/disableGetUserMedia.html

index 2f8254b..a7ebd69 100644 (file)
@@ -1,3 +1,17 @@
+2018-05-18  Eric Carlson  <eric.carlson@apple.com>
+
+        Handle failure to extend sandbox gracefully
+        https://bugs.webkit.org/show_bug.cgi?id=185779
+        <rdar://problem/40316349>
+
+        Reviewed by Brent Fulgham.
+
+        Test: TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm
+
+        * Modules/mediastream/UserMediaRequest.cpp:
+        (WebCore::UserMediaRequest::deny): Include the error string in the promise rejection.
+        * Modules/mediastream/UserMediaRequest.h:
+
 2018-05-18  Antoine Quint  <graouts@apple.com>
 
         [Web Animations] Turn Web Animations with CSS integration on for test runners
index 6c20be1..00ec3ed 100644 (file)
@@ -257,45 +257,51 @@ void UserMediaRequest::allow(CaptureDevice&& audioDevice, CaptureDevice&& videoD
 #endif
 }
 
-void UserMediaRequest::deny(MediaAccessDenialReason reason, const String& invalidConstraint)
+void UserMediaRequest::deny(MediaAccessDenialReason reason, const String& message)
 {
     if (!m_scriptExecutionContext)
         return;
 
+    ExceptionCode code;
     switch (reason) {
     case MediaAccessDenialReason::NoConstraints:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - no constraints");
-        m_promise.reject(TypeError);
+        code = TypeError;
         break;
     case MediaAccessDenialReason::UserMediaDisabled:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - user media disabled");
-        m_promise.reject(SecurityError);
+        code = SecurityError;
         break;
     case MediaAccessDenialReason::NoCaptureDevices:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - no capture devices");
-        m_promise.reject(NotFoundError);
+        code = NotFoundError;
         break;
     case MediaAccessDenialReason::InvalidConstraint:
-        RELEASE_LOG(MediaStream, "UserMediaRequest::deny - invalid constraint - %s", invalidConstraint.utf8().data());
-        m_promise.rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(invalidConstraint, ASCIILiteral("Invalid constraint")).get());
-        break;
+        RELEASE_LOG(MediaStream, "UserMediaRequest::deny - invalid constraint - %s", message.utf8().data());
+        m_promise.rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(message, ASCIILiteral("Invalid constraint")).get());
+        return;
     case MediaAccessDenialReason::HardwareError:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - hardware error");
-        m_promise.reject(NotReadableError);
+        code = NotReadableError;
         break;
     case MediaAccessDenialReason::OtherFailure:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - other failure");
-        m_promise.reject(AbortError);
+        code = AbortError;
         break;
     case MediaAccessDenialReason::PermissionDenied:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - permission denied");
-        m_promise.reject(NotAllowedError);
+        code = NotAllowedError;
         break;
     case MediaAccessDenialReason::InvalidAccess:
         RELEASE_LOG(MediaStream, "UserMediaRequest::deny - invalid access");
-        m_promise.reject(InvalidAccessError);
+        code = InvalidAccessError;
         break;
     }
+
+    if (!message.isEmpty())
+        m_promise.reject(code, message);
+    else
+        m_promise.reject(code);
 }
 
 void UserMediaRequest::stop()
index 6804da2..2da682f 100644 (file)
@@ -57,7 +57,7 @@ public:
     WEBCORE_EXPORT void allow(CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, String&& deviceIdentifierHashSalt);
 
     enum MediaAccessDenialReason { NoConstraints, UserMediaDisabled, NoCaptureDevices, InvalidConstraint, HardwareError, PermissionDenied, InvalidAccess, OtherFailure };
-    WEBCORE_EXPORT void deny(MediaAccessDenialReason, const String& invalidConstraint = emptyString());
+    WEBCORE_EXPORT void deny(MediaAccessDenialReason, const String& errorMessage = emptyString());
 
     const Vector<String>& audioDeviceUIDs() const { return m_audioDeviceUIDs; }
     const Vector<String>& videoDeviceUIDs() const { return m_videoDeviceUIDs; }
index f910418..5b8d1f6 100644 (file)
@@ -1,3 +1,28 @@
+2018-05-18  Eric Carlson  <eric.carlson@apple.com>
+
+        Handle failure to extend sandbox gracefully
+        https://bugs.webkit.org/show_bug.cgi?id=185779
+        <rdar://problem/40316349>
+
+        Reviewed by Brent Fulgham.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _denyNextUserMediaRequest]): 
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+
+        * UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
+        (WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted): Don't append
+        the request to m_grantedRequests if it failed.
+        (WebKit::UserMediaPermissionRequestManagerProxy::grantAccess): Deny request if willCreateMediaStream
+        fails.
+        * UIProcess/UserMediaPermissionRequestManagerProxy.h:
+
+        * UIProcess/UserMediaProcessManager.cpp:
+        (WebKit::UserMediaProcessManager::willCreateMediaStream): Don't try to extend sandbox if
+        we fail to allocate all necessary handles.
+        * UIProcess/UserMediaProcessManager.h:
+        (WebKit::UserMediaProcessManager::denyNextUserMediaRequest): New, for testing.
+
 2018-05-18  Antoine Quint  <graouts@apple.com>
 
         [Web Animations] Turn Web Animations with CSS integration on for test runners
index 23bcfd7..3a4df7d 100644 (file)
@@ -47,6 +47,7 @@
 #import "RemoteObjectRegistryMessages.h"
 #import "SandboxUtilities.h"
 #import "UIDelegate.h"
+#import "UserMediaProcessManager.h"
 #import "VersionChecks.h"
 #import "ViewGestureController.h"
 #import "ViewSnapshotStore.h"
@@ -6385,6 +6386,10 @@ static WebCore::UserInterfaceLayoutDirection toUserInterfaceLayoutDirection(UISe
     _page->setDefersLoadingForTesting(defersLoading);
 }
 
+- (void)_denyNextUserMediaRequest
+{
+    WebKit::UserMediaProcessManager::singleton().denyNextUserMediaRequest();
+}
 @end
 
 
index 049d90a..a5721db 100644 (file)
@@ -459,6 +459,8 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) {
 - (void)_disableBackForwardSnapshotVolatilityForTesting WK_API_AVAILABLE(macosx(10.12.3), ios(10.3));
 - (void)_executeEditCommand:(NSString *)command argument:(NSString *)argument completion:(void (^)(BOOL))completion WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
 
+- (void)_denyNextUserMediaRequest WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 - (BOOL)_beginBackSwipeForTesting;
 - (BOOL)_completeBackSwipeForTesting;
 - (void)_setDefersLoadingForTesting:(BOOL)defersLoading;
index 26b8661..3c07295 100644 (file)
@@ -153,8 +153,9 @@ void UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted(uint64_t
     if (!request)
         return;
 
-    grantAccess(userMediaID, WTFMove(audioDevice), WTFMove(videoDevice), request->deviceIdentifierHashSalt());
-    m_grantedRequests.append(request.releaseNonNull());
+    if (grantAccess(userMediaID, WTFMove(audioDevice), WTFMove(videoDevice), request->deviceIdentifierHashSalt()))
+        m_grantedRequests.append(request.releaseNonNull());
+
 #else
     UNUSED_PARAM(userMediaID);
     UNUSED_PARAM(audioDevice);
@@ -220,10 +221,15 @@ bool UserMediaPermissionRequestManagerProxy::wasRequestDenied(uint64_t mainFrame
     return false;
 }
 
-void UserMediaPermissionRequestManagerProxy::grantAccess(uint64_t userMediaID, const CaptureDevice audioDevice, const CaptureDevice videoDevice, const String& deviceIdentifierHashSalt)
+bool UserMediaPermissionRequestManagerProxy::grantAccess(uint64_t userMediaID, const CaptureDevice audioDevice, const CaptureDevice videoDevice, const String& deviceIdentifierHashSalt)
 {
-    UserMediaProcessManager::singleton().willCreateMediaStream(*this, !!audioDevice, !!videoDevice);
+    if (!UserMediaProcessManager::singleton().willCreateMediaStream(*this, !!audioDevice, !!videoDevice)) {
+        denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure, "Unable to extend sandbox.");
+        return false;
+    }
+
     m_page.process().send(Messages::WebPage::UserMediaAccessWasGranted(userMediaID, audioDevice, videoDevice, deviceIdentifierHashSalt), m_page.pageID());
+    return true;
 }
 #endif
 
index ab15b49..573af04 100644 (file)
@@ -68,7 +68,7 @@ private:
     Ref<UserMediaPermissionRequestProxy> createPermissionRequest(uint64_t userMediaID, uint64_t mainFrameID, uint64_t frameID, Ref<WebCore::SecurityOrigin>&& userMediaDocumentOrigin, Ref<WebCore::SecurityOrigin>&& topLevelDocumentOrigin, Vector<WebCore::CaptureDevice>&& audioDevices, Vector<WebCore::CaptureDevice>&& videoDevices, String&&, WebCore::MediaStreamRequest&&);
     void denyRequest(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason, const String& invalidConstraint);
 #if ENABLE(MEDIA_STREAM)
-    void grantAccess(uint64_t userMediaID, const WebCore::CaptureDevice audioDevice, const WebCore::CaptureDevice videoDevice, const String& deviceIdentifierHashSalt);
+    bool grantAccess(uint64_t userMediaID, const WebCore::CaptureDevice audioDevice, const WebCore::CaptureDevice videoDevice, const String& deviceIdentifierHashSalt);
 
     const UserMediaPermissionRequestProxy* searchForGrantedRequest(uint64_t frameID, const WebCore::SecurityOrigin& userMediaDocumentOrigin, const WebCore::SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo) const;
     bool wasRequestDenied(uint64_t mainFrameID, const WebCore::SecurityOrigin& userMediaDocumentOrigin, const WebCore::SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo);
index 89224c4..94a0b46 100644 (file)
@@ -126,14 +126,17 @@ void UserMediaProcessManager::muteCaptureMediaStreamsExceptIn(WebPageProxy& page
 #endif
 }
 
-void UserMediaProcessManager::willCreateMediaStream(UserMediaPermissionRequestManagerProxy& proxy, bool withAudio, bool withVideo)
+bool UserMediaProcessManager::willCreateMediaStream(UserMediaPermissionRequestManagerProxy& proxy, bool withAudio, bool withVideo)
 {
 #if ENABLE(SANDBOX_EXTENSIONS)
     auto& processStartingCapture = proxy.page().process();
 
     ASSERT(stateMap().contains(&processStartingCapture));
 
-    proxy.page().activateMediaStreamCaptureInPage();
+    if (m_denyNextRequest) {
+        m_denyNextRequest = false;
+        return false;
+    }
 
     auto& state = processState(processStartingCapture);
     size_t extensionCount = 0;
@@ -171,10 +174,23 @@ void UserMediaProcessManager::willCreateMediaStream(UserMediaPermissionRequestMa
             }
         }
 
+        if (ids.size() != handles.size()) {
+            WTFLogAlways("Could not create a required sandbox extension, capture will fail!");
+            return false;
+        }
+
         state.setSandboxExtensionsGranted(currentExtensions);
         processStartingCapture.send(Messages::WebPage::GrantUserMediaDeviceSandboxExtensions(MediaDeviceSandboxExtensions(ids, WTFMove(handles))), proxy.page().pageID());
     }
+#else
+    UNUSED_PARAM(proxy);
+    UNUSED_PARAM(withAudio);
+    UNUSED_PARAM(withVideo);
 #endif
+
+    proxy.page().activateMediaStreamCaptureInPage();
+
+    return true;
 }
 
 void UserMediaProcessManager::startedCaptureSession(UserMediaPermissionRequestManagerProxy& proxy)
index e14252f..81998b4 100644 (file)
@@ -34,7 +34,7 @@ public:
     void addUserMediaPermissionRequestManagerProxy(UserMediaPermissionRequestManagerProxy&);
     void removeUserMediaPermissionRequestManagerProxy(UserMediaPermissionRequestManagerProxy&);
 
-    void willCreateMediaStream(UserMediaPermissionRequestManagerProxy&, bool withAudio, bool withVideo);
+    bool willCreateMediaStream(UserMediaPermissionRequestManagerProxy&, bool withAudio, bool withVideo);
     void muteCaptureMediaStreamsExceptIn(WebPageProxy&);
 
     void startedCaptureSession(UserMediaPermissionRequestManagerProxy&);
@@ -43,8 +43,11 @@ public:
     void setCaptureEnabled(bool);
     bool captureEnabled() const { return m_captureEnabled; }
 
+    void denyNextUserMediaRequest() { m_denyNextRequest = true; }
+
 private:
     bool m_captureEnabled { true };
+    bool m_denyNextRequest { false };
 };
 
 } // namespace WebKit
index b94e2e9..21dd8f9 100644 (file)
@@ -1,3 +1,21 @@
+2018-05-18  Eric Carlson  <eric.carlson@apple.com>
+
+        Handle failure to extend sandbox gracefully
+        https://bugs.webkit.org/show_bug.cgi?id=185779
+        <rdar://problem/40316349>
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm: Added.
+        (-[SimulateFailedSandboxMessageHandler userContentController:didReceiveScriptMessage:]):
+        (-[SimulateFailedSandboxUIDelegate _webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:]):
+        (-[SimulateFailedSandboxUIDelegate _webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:]):
+        (MediaCaptureSimulateFailedSandbox::SetUp):
+        (MediaCaptureSimulateFailedSandbox::loadTestAndWaitForMessage):
+        (TEST_F):
+        * TestWebKitAPI/Tests/WebKitCocoa/disableGetUserMedia.html:
+
 2018-05-18  Daniel Bates  <dabates@apple.com>
 
         "debug-safari --ios-simulator" creates lldb target to launch Mac Safari
index b20baac..0372673 100644 (file)
@@ -35,6 +35,7 @@
                07E1F6A21FFC44FA0096C7EC /* getDisplayMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 07E1F6A11FFC44F90096C7EC /* getDisplayMedia.html */; };
                07E1F6A31FFC4B760096C7EC /* GetDisplayMedia.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07E1F6A01FFC3A080096C7EC /* GetDisplayMedia.mm */; };
                07E499911F9E56DF002F1EF3 /* GetUserMediaReprompt.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07E499901F9E56A1002F1EF3 /* GetUserMediaReprompt.mm */; };
+               07F4E92E20AF59E2002E3803 /* UserMediaSimulateFailedSandbox.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07F4E92D20AF58D3002E3803 /* UserMediaSimulateFailedSandbox.mm */; };
                0EBBCC661FFF9E0C00FA42AB /* pop-up-check.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0EBBCC651FFF9DCE00FA42AB /* pop-up-check.html */; };
                0F139E771A423A5B00F590F5 /* WeakObjCPtr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E751A423A5300F590F5 /* WeakObjCPtr.mm */; };
                0F139E781A423A6B00F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
                07E1F6A11FFC44F90096C7EC /* getDisplayMedia.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = getDisplayMedia.html; path = ../WebKit/getDisplayMedia.html; sourceTree = "<group>"; };
                07E499901F9E56A1002F1EF3 /* GetUserMediaReprompt.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GetUserMediaReprompt.mm; sourceTree = "<group>"; };
                07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UserMediaDisabled.mm; sourceTree = "<group>"; };
+               07F4E92D20AF58D3002E3803 /* UserMediaSimulateFailedSandbox.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UserMediaSimulateFailedSandbox.mm; sourceTree = "<group>"; };
                0BCD833414857CE400EA2003 /* HashMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HashMap.cpp; sourceTree = "<group>"; };
                0BCD85691485C98B00EA2003 /* SetForScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SetForScope.cpp; sourceTree = "<group>"; };
                0EBBCC651FFF9DCE00FA42AB /* pop-up-check.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "pop-up-check.html"; sourceTree = "<group>"; };
                                7C882E051C80C624006BF731 /* UserContentWorldProtocol.h */,
                                7CCB99201D3B41F6003922F6 /* UserInitiatedActionInNavigationAction.mm */,
                                07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */,
+                               07F4E92D20AF58D3002E3803 /* UserMediaSimulateFailedSandbox.mm */,
                                93E943F11CD3E87E00AC08C2 /* VideoControlsManager.mm */,
                                6356FB211EC4E0BA0044BF18 /* VisibleContentRect.mm */,
                                83779C371F82FEB0007CDA8A /* VisitedLinkStore.mm */,
                                7CCB99211D3B41F6003922F6 /* UserInitiatedActionInNavigationAction.mm in Sources */,
                                7CCE7F171A411AE600447C4C /* UserMedia.cpp in Sources */,
                                0799C3491EBA2D7B003B7532 /* UserMediaDisabled.mm in Sources */,
+                               07F4E92E20AF59E2002E3803 /* UserMediaSimulateFailedSandbox.mm in Sources */,
                                7CCE7F181A411AE600447C4C /* UserMessage.cpp in Sources */,
                                7C83E03A1D0A602700FEBCF3 /* UtilitiesCocoa.mm in Sources */,
                                7C83E0C61D0A654E00FEBCF3 /* VideoControlsManager.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserMediaSimulateFailedSandbox.mm
new file mode 100644 (file)
index 0000000..07d5779
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestWKWebView.h"
+
+#import <WebKit/WKPreferencesPrivate.h>
+#import <WebKit/WKUserContentControllerPrivate.h>
+#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/WebKit.h>
+#import <WebKit/_WKProcessPoolConfiguration.h>
+#import <wtf/RetainPtr.h>
+
+#if WK_API_ENABLED
+
+static bool refuseRequest = false;
+static bool wasPrompted = false;
+
+static bool receivedScriptMessage = false;
+static RetainPtr<WKScriptMessage> lastScriptMessage;
+
+@interface SimulateFailedSandboxMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation SimulateFailedSandboxMessageHandler
+
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    lastScriptMessage = message;
+    receivedScriptMessage = true;
+}
+@end
+
+@interface SimulateFailedSandboxUIDelegate : NSObject <WKUIDelegate>
+@end
+
+@implementation SimulateFailedSandboxUIDelegate
+
+- (void)_webView:(WKWebView *)webView requestUserMediaAuthorizationForDevices:(NSUInteger)devices url:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL decisionHandler:(void (^)(BOOL))decisionHandler
+{
+    wasPrompted = true;
+
+    if (refuseRequest) {
+        decisionHandler(NO);
+        return;
+    }
+
+    BOOL needsMicrophoneAuthorized = devices & _WKCaptureDeviceMicrophone;
+    BOOL needsCameraAuthorized = devices & _WKCaptureDeviceCamera;
+    if (!needsMicrophoneAuthorized && !needsCameraAuthorized) {
+        decisionHandler(NO);
+        return;
+    }
+
+    decisionHandler(YES);
+}
+
+- (void)_webView:(WKWebView *)webView checkUserMediaPermissionForURL:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL frameIdentifier:(NSUInteger)frameIdentifier decisionHandler:(void (^)(NSString *, BOOL))decisionHandler
+{
+    if (refuseRequest) {
+        decisionHandler(nil, NO);
+        return;
+    }
+
+    decisionHandler(@"0x987654321", NO);
+}
+@end
+
+class MediaCaptureSimulateFailedSandbox : public testing::Test {
+public:
+    virtual void SetUp()
+    {
+        m_configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+        RetainPtr<SimulateFailedSandboxMessageHandler> handler = adoptNS([[SimulateFailedSandboxMessageHandler alloc] init]);
+        [[m_configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
+
+        m_webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:m_configuration.get()]);
+
+        auto preferences = [m_webView configuration].preferences;
+        preferences._mediaDevicesEnabled = YES;
+        preferences._mockCaptureDevicesEnabled = YES;
+        preferences._mediaCaptureRequiresSecureConnection = NO;
+
+        m_uiDelegate = adoptNS([[SimulateFailedSandboxUIDelegate alloc] init]);
+        [m_webView setUIDelegate:m_uiDelegate.get()];
+    }
+
+    void loadTestAndWaitForMessage(const char* message)
+    {
+        wasPrompted = false;
+        receivedScriptMessage = false;
+        [m_webView loadTestPageNamed:@"disableGetUserMedia"];
+        TestWebKitAPI::Util::run(&receivedScriptMessage);
+        EXPECT_STREQ([(NSString *)[lastScriptMessage body] UTF8String], message);
+    }
+
+    RetainPtr<WKWebViewConfiguration> m_configuration;
+    RetainPtr<SimulateFailedSandboxUIDelegate> m_uiDelegate;
+    RetainPtr<TestWKWebView> m_webView;
+};
+
+TEST_F(MediaCaptureSimulateFailedSandbox, Test)
+{
+    EXPECT_TRUE(m_webView.get()._mediaCaptureEnabled);
+    loadTestAndWaitForMessage("allowed");
+    EXPECT_TRUE(wasPrompted);
+
+    [m_webView _denyNextUserMediaRequest];
+    loadTestAndWaitForMessage("denied");
+    EXPECT_TRUE(wasPrompted);
+    auto* error = [m_webView stringByEvaluatingJavaScript:@"lastError()"];
+    EXPECT_STREQ([error UTF8String], "AbortError,Unable to extend sandbox");
+
+    loadTestAndWaitForMessage("allowed");
+    EXPECT_TRUE(wasPrompted);
+}
+#endif
index bb61d9b..3953cd1 100644 (file)
@@ -1,4 +1,5 @@
 <script>
+    let err = '';
     function gotUserMedia(mediaStream)
     {
         window.webkit.messageHandlers.testHandler.postMessage('allowed');
@@ -6,10 +7,16 @@
 
     function userMediaError(error)
     {
+        err = `${error.name},${error.message}`;
         window.webkit.messageHandlers.testHandler.postMessage('denied');
     }
 
-    var constraints = { audio: false, video: true};
+    function lastError()
+    {
+        return err;
+    }
+
+    let constraints = { audio: false, video: true};
     navigator.mediaDevices.getUserMedia(constraints)
         .then(gotUserMedia).
         catch(userMediaError);