Diff
Modified: trunk/Source/WebKit2/ChangeLog (219134 => 219135)
--- trunk/Source/WebKit2/ChangeLog 2017-07-05 16:51:59 UTC (rev 219134)
+++ trunk/Source/WebKit2/ChangeLog 2017-07-05 17:41:47 UTC (rev 219135)
@@ -1,3 +1,20 @@
+2017-07-05 Eric Carlson <eric.carl...@apple.com>
+
+ [MediaStream] Protect request and web view during gUM client callback
+ https://bugs.webkit.org/show_bug.cgi?id=174096
+ <rdar://problem/32833102>
+
+ Reviewed by Youenn Fablet.
+
+ Retain the message and WebView during asynchronous calls so they won't be
+ released if a navigation happens during a call to the UA for getUserMedia
+ or enumerateMediaDevices.
+
+ * UIProcess/Cocoa/UIDelegate.mm:
+ (WebKit::requestUserMediaAuthorizationForDevices):
+ (WebKit::UIDelegate::UIClient::decidePolicyForUserMediaPermissionRequest):
+ (WebKit::UIDelegate::UIClient::checkUserMediaPermissionForOrigin):
+
2017-07-05 Matt Lewis <jlew...@apple.com>
Unreviewed, rolling out r219128.
Modified: trunk/Source/WebKit2/UIProcess/Cocoa/UIDelegate.mm (219134 => 219135)
--- trunk/Source/WebKit2/UIProcess/Cocoa/UIDelegate.mm 2017-07-05 16:51:59 UTC (rev 219134)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/UIDelegate.mm 2017-07-05 17:41:47 UTC (rev 219135)
@@ -403,6 +403,32 @@
}
#endif
+static void requestUserMediaAuthorizationForDevices(const WebKit::WebFrameProxy& frame, WebKit::UserMediaPermissionRequestProxy& request, id <WKUIDelegatePrivate> delegate, WKWebView& webView)
+{
+ auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([protectedRequest = makeRef(request)](BOOL authorized) {
+ if (!authorized) {
+ protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
+ return;
+ }
+ const String& videoDeviceUID = protectedRequest->requiresVideo() ? protectedRequest->videoDeviceUIDs().first() : String();
+ const String& audioDeviceUID = protectedRequest->requiresAudio() ? protectedRequest->audioDeviceUIDs().first() : String();
+ protectedRequest->allow(audioDeviceUID, videoDeviceUID);
+ });
+
+ const WebFrameProxy* mainFrame = frame.page()->mainFrame();
+ WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
+ WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
+
+ _WKCaptureDevices devices = 0;
+ if (request.requiresAudio())
+ devices |= _WKCaptureDeviceMicrophone;
+ if (request.requiresVideo())
+ devices |= _WKCaptureDeviceCamera;
+
+ auto protectedWebView = RetainPtr<WKWebView>(&webView);
+ [delegate _webView:protectedWebView.get() requestUserMediaAuthorizationForDevices:devices url:requestFrameURL mainFrameURL:mainFrameURL decisionHandler:decisionHandler.get()];
+}
+
bool UIDelegate::UIClient::decidePolicyForUserMediaPermissionRequest(WebKit::WebPageProxy& page, WebKit::WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, WebKit::UserMediaPermissionRequestProxy& request)
{
auto delegate = m_uiDelegate.m_delegate.get();
@@ -418,77 +444,62 @@
return true;
}
- __block WKWebView *webView = m_uiDelegate.m_webView;
- void (^uiDelegateAuthorizationBlock)(void) = ^ {
- const WebFrameProxy* mainFrame = frame.page()->mainFrame();
- WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
- WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
+#if PLATFORM(IOS)
+ auto requestCameraAuthorization = BlockPtr<void()>::fromCallable([this, &frame, protectedRequest = makeRef(request), webView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)]() {
- _WKCaptureDevices devices = 0;
- if (requiresAudio)
- devices |= _WKCaptureDeviceMicrophone;
- if (requiresVideo)
- devices |= _WKCaptureDeviceCamera;
+ if (!protectedRequest->requiresVideo()) {
+ requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
+ return;
+ }
+ AVAuthorizationStatus cameraAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeVideo()];
+ switch (cameraAuthorizationStatus) {
+ case AVAuthorizationStatusAuthorized:
+ requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
+ break;
+ case AVAuthorizationStatusDenied:
+ case AVAuthorizationStatusRestricted:
+ protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
+ return;
+ case AVAuthorizationStatusNotDetermined:
+ auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([this, &frame, protectedRequest = makeRef(protectedRequest.get()), webView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)](BOOL authorized) {
+ if (!authorized) {
+ protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
+ return;
+ }
+ requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
+ });
- [(id <WKUIDelegatePrivate>)delegate _webView:webView requestUserMediaAuthorizationForDevices:devices url:requestFrameURL mainFrameURL:mainFrameURL decisionHandler:^(BOOL authorized) {
- if (!authorized) {
- request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
- return;
- }
- const String& videoDeviceUID = requiresVideo ? request.videoDeviceUIDs().first() : String();
- const String& audioDeviceUID = requiresAudio ? request.audioDeviceUIDs().first() : String();
- request.allow(audioDeviceUID, videoDeviceUID);
- }];
- };
+ [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeVideo() completionHandler:decisionHandler.get()];
+ break;
+ }
+ });
-#if PLATFORM(IOS)
- void (^cameraAuthorizationBlock)(void) = ^ {
- if (requiresVideo) {
- AVAuthorizationStatus cameraAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeVideo()];
- switch (cameraAuthorizationStatus) {
- case AVAuthorizationStatusDenied:
- case AVAuthorizationStatusRestricted:
- request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
- return;
- case AVAuthorizationStatusNotDetermined:
- [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeVideo() completionHandler:^(BOOL authorized) {
- if (!authorized) {
- request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
- return;
- }
- uiDelegateAuthorizationBlock();
- }];
- break;
- default:
- uiDelegateAuthorizationBlock();
- }
- } else
- uiDelegateAuthorizationBlock();
- };
-
if (requiresAudio) {
AVAuthorizationStatus microphoneAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeAudio()];
switch (microphoneAuthorizationStatus) {
+ case AVAuthorizationStatusAuthorized:
+ requestCameraAuthorization();
+ break;
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
return true;
case AVAuthorizationStatusNotDetermined:
- [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeAudio() completionHandler:^(BOOL authorized) {
+ auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([protectedRequest = makeRef(request), requestCameraAuthorization](BOOL authorized) {
if (!authorized) {
- request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
+ protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
return;
}
- cameraAuthorizationBlock();
- }];
+ requestCameraAuthorization();
+ });
+
+ [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeAudio() completionHandler:decisionHandler.get()];
break;
- default:
- cameraAuthorizationBlock();
}
} else
- cameraAuthorizationBlock();
+ requestCameraAuthorization();
#else
- uiDelegateAuthorizationBlock();
+ requestUserMediaAuthorizationForDevices(frame, request, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *m_uiDelegate.m_webView);
#endif
return true;
@@ -507,10 +518,12 @@
WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
- [(id <WKUIDelegatePrivate>)delegate _webView:webView checkUserMediaPermissionForURL:requestFrameURL mainFrameURL:mainFrameURL frameIdentifier:frame.frameID() decisionHandler:^(NSString *salt, BOOL authorized) {
- request.setUserMediaAccessInfo(String(salt), authorized);
- }];
+ auto decisionHandler = BlockPtr<void(NSString *, BOOL)>::fromCallable([protectedRequest = makeRef(request)](NSString *salt, BOOL authorized) {
+ protectedRequest->setUserMediaAccessInfo(String(salt), authorized);
+ });
+ [(id <WKUIDelegatePrivate>)delegate _webView:webView checkUserMediaPermissionForURL:requestFrameURL mainFrameURL:mainFrameURL frameIdentifier:frame.frameID() decisionHandler:decisionHandler.get()];
+
return true;
}
Modified: trunk/Tools/ChangeLog (219134 => 219135)
--- trunk/Tools/ChangeLog 2017-07-05 16:51:59 UTC (rev 219134)
+++ trunk/Tools/ChangeLog 2017-07-05 17:41:47 UTC (rev 219135)
@@ -1,3 +1,14 @@
+2017-07-05 Eric Carlson <eric.carl...@apple.com>
+
+ [MediaStream] Protect request and web view during gUM client callback
+ https://bugs.webkit.org/show_bug.cgi?id=174096
+ <rdar://problem/32833102>
+
+ Reviewed by Youenn Fablet.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebKit2/GetUserMediaNavigation.mm: New test.
+
2017-07-05 Wenson Hsieh <wenson_hs...@apple.com>
Unreviewed, guard iOS 11-dependent UIPasteboardTests to be iOS 11+ only
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (219134 => 219135)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-07-05 16:51:59 UTC (rev 219134)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-07-05 17:41:47 UTC (rev 219135)
@@ -28,6 +28,7 @@
0799C3491EBA2D7B003B7532 /* UserMediaDisabled.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */; };
0799C34B1EBA3301003B7532 /* disableGetUserMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */; };
07C046CA1E4262A8007201E7 /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C046C91E42573E007201E7 /* CARingBuffer.cpp */; };
+ 07CE1CF31F06A7E000BF89F5 /* GetUserMediaNavigation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07CE1CF21F06A7E000BF89F5 /* GetUserMediaNavigation.mm */; };
0F139E771A423A5B00F590F5 /* WeakObjCPtr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E751A423A5300F590F5 /* WeakObjCPtr.mm */; };
0F139E781A423A6B00F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
0F139E791A42457000F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
@@ -960,6 +961,7 @@
0766DD1F1A5AD5200023E3BB /* PendingAPIRequestURL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PendingAPIRequestURL.cpp; sourceTree = "<group>"; };
0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = disableGetUserMedia.html; sourceTree = "<group>"; };
07C046C91E42573E007201E7 /* CARingBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CARingBuffer.cpp; sourceTree = "<group>"; };
+ 07CE1CF21F06A7E000BF89F5 /* GetUserMediaNavigation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GetUserMediaNavigation.mm; sourceTree = "<group>"; };
07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UserMediaDisabled.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>"; };
@@ -2240,6 +2242,7 @@
26F52EAA182872600023D412 /* Geolocation.cpp */,
F660AA0C15A5F061003A1243 /* GetInjectedBundleInitializationUserDataCallback.cpp */,
F660AA0F15A5F624003A1243 /* GetInjectedBundleInitializationUserDataCallback_Bundle.cpp */,
+ 07CE1CF21F06A7E000BF89F5 /* GetUserMediaNavigation.mm */,
4BFDFFA8131477770061F24B /* HitTestResultNodeHandle.cpp */,
4BFDFFA61314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp */,
BC575AAC126E83B9006F0F12 /* InjectedBundleBasic.cpp */,
@@ -3109,6 +3112,7 @@
7A6A2C701DCCFA8C00C0D085 /* LocalStorageQuirkTest.mm in Sources */,
7CCE7EB21A411A5100447C4C /* MemoryCacheAddImageToCacheIOS.mm in Sources */,
7CCE7EC51A411A7E00447C4C /* MemoryCacheDisableWithinResourceLoadDelegate.mm in Sources */,
+ 07CE1CF31F06A7E000BF89F5 /* GetUserMediaNavigation.mm in Sources */,
7CCE7EC61A411A7E00447C4C /* MemoryCachePruneWithinResourceLoadDelegate.mm in Sources */,
CDA315981ED53651009F60D3 /* MediaPlaybackSleepAssertion.mm in Sources */,
5C0BF88D1DD5964D00B00328 /* MemoryPressureHandler.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2/GetUserMediaNavigation.mm (0 => 219135)
--- trunk/Tools/TestWebKitAPI/Tests/WebKit2/GetUserMediaNavigation.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2/GetUserMediaNavigation.mm 2017-07-05 17:41:47 UTC (rev 219135)
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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"
+
+#if WK_API_ENABLED
+
+#if ENABLE(MEDIA_STREAM)
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKPreferencesPrivate.h>
+#import <WebKit/WKUIDelegatePrivate.h>
+#import <WebKit/WKWebView.h>
+#import <WebKit/WKWebViewConfiguration.h>
+#import <WebKit/_WKProcessPoolConfiguration.h>
+
+static bool okToProceed = false;
+static bool shouldReleaseInEnumerate = false;
+
+@interface NavigationWhileGetUserMediaPromptDisplayedUIDelegate : NSObject<WKUIDelegate>
+- (void)_webView:(WKWebView *)webView requestUserMediaAuthorizationForDevices:(_WKCaptureDevices)devices url:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL decisionHandler:(void (^)(BOOL authorized))decisionHandler;
+- (void)_webView:(WKWebView *)webView checkUserMediaPermissionForURL:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL frameIdentifier:(NSUInteger)frameIdentifier decisionHandler:(void (^)(NSString *salt, BOOL authorized))decisionHandler;
+@end
+
+@implementation NavigationWhileGetUserMediaPromptDisplayedUIDelegate
+- (void)_webView:(WKWebView *)webView requestUserMediaAuthorizationForDevices:(_WKCaptureDevices)devices url:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL decisionHandler:(void (^)(BOOL authorized))decisionHandler
+{
+ [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
+ [webView release];
+ okToProceed = true;
+ decisionHandler(YES);
+}
+
+- (void)_webView:(WKWebView *)webView checkUserMediaPermissionForURL:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL frameIdentifier:(NSUInteger)frameIdentifier decisionHandler:(void (^)(NSString *salt, BOOL authorized))decisionHandler
+{
+ [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
+ if (shouldReleaseInEnumerate)
+ [webView release];
+ okToProceed = true;
+ decisionHandler(@"0x987654321", YES);
+}
+@end
+
+namespace TestWebKitAPI {
+
+TEST(WebKit2, NavigateDuringGetUserMediaPrompt)
+{
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ auto processPoolConfig = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+ auto preferences = [configuration preferences];
+ preferences._mediaCaptureRequiresSecureConnection = NO;
+ preferences._mediaDevicesEnabled = YES;
+ preferences._mockCaptureDevicesEnabled = YES;
+ auto webView = [[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get() processPoolConfiguration:processPoolConfig.get()];
+ auto delegate = adoptNS([[NavigationWhileGetUserMediaPromptDisplayedUIDelegate alloc] init]);
+ webView.UIDelegate = delegate.get();
+
+ shouldReleaseInEnumerate = false;
+ [webView loadTestPageNamed:@"getUserMedia"];
+ TestWebKitAPI::Util::run(&okToProceed);
+}
+
+TEST(WebKit2, NavigateDuringDeviceEnumeration)
+{
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ auto processPoolConfig = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+ auto preferences = [configuration preferences];
+ preferences._mediaCaptureRequiresSecureConnection = NO;
+ preferences._mediaDevicesEnabled = YES;
+ preferences._mockCaptureDevicesEnabled = YES;
+ auto webView = [[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get() processPoolConfiguration:processPoolConfig.get()];
+ auto delegate = adoptNS([[NavigationWhileGetUserMediaPromptDisplayedUIDelegate alloc] init]);
+ webView.UIDelegate = delegate.get();
+
+ shouldReleaseInEnumerate = true;
+ [webView loadTestPageNamed:@"enumerateMediaDevices"];
+ TestWebKitAPI::Util::run(&okToProceed);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(MEDIA_STREAM)
+
+#endif // WK_API_ENABLED