[iOS] Accessibility crashing because MediaPlayer is laying out UI off the main thread
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2015 22:31:46 +0000 (22:31 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2015 22:31:46 +0000 (22:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142970

Reviewed by Eric Carlson.

isMainThread() will (ironically) return true if called from the web thread. Rather than dispatch
synchronously to the main thread to allocate the _volumeView, dispatch asynchronously and handle
the case where the MPVolumeView has not yet been created.

* platform/audio/ios/MediaSessionManagerIOS.mm:
(-[WebMediaSessionHelper allocateVolumeView]): Dispatch to the main thread to allocate. Move notification
    registration to -setVolumeView:.
(-[WebMediaSessionHelper setVolumeView:]): Added. Register/Unregister for route availablitiy notifications.
(-[WebMediaSessionHelper hasWirelessTargetsAvailable]): Handle the possibility of a nil _volumeView.

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

Source/WebCore/ChangeLog
Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm

index 5b0fb52..174b75b 100644 (file)
@@ -1,3 +1,20 @@
+2015-03-26  Jer Noble  <jer.noble@apple.com>
+
+        [iOS] Accessibility crashing because MediaPlayer is laying out UI off the main thread
+        https://bugs.webkit.org/show_bug.cgi?id=142970
+
+        Reviewed by Eric Carlson.
+
+        isMainThread() will (ironically) return true if called from the web thread. Rather than dispatch
+        synchronously to the main thread to allocate the _volumeView, dispatch asynchronously and handle
+        the case where the MPVolumeView has not yet been created.
+
+        * platform/audio/ios/MediaSessionManagerIOS.mm:
+        (-[WebMediaSessionHelper allocateVolumeView]): Dispatch to the main thread to allocate. Move notification
+            registration to -setVolumeView:.
+        (-[WebMediaSessionHelper setVolumeView:]): Added. Register/Unregister for route availablitiy notifications.
+        (-[WebMediaSessionHelper hasWirelessTargetsAvailable]): Handle the possibility of a nil _volumeView.
+
 2015-03-26  Benjamin Poulain  <bpoulain@apple.com>
 
         Fix state maching debugging after r181964
index 6600481..91552e5 100644 (file)
@@ -97,6 +97,7 @@ using namespace WebCore;
 
 - (id)initWithCallback:(MediaSessionManageriOS*)callback;
 - (void)allocateVolumeView;
+- (void)setVolumeView:(RetainPtr<MPVolumeView>)volumeView;
 - (void)clearCallback;
 - (void)interruption:(NSNotification *)notification;
 - (void)applicationWillEnterForeground:(NSNotification *)notification;
@@ -245,19 +246,29 @@ void MediaSessionManageriOS::externalOutputDeviceAvailableDidChange()
 
 - (void)allocateVolumeView
 {
-    if (!isMainThread()) {
-        // Call synchronously to the main thread so that _volumeView will be completely setup before the constructor completes
-        // because hasWirelessTargetsAvailable is synchronous and can be called on the WebThread.
-        RetainPtr<WebMediaSessionHelper> strongSelf = self;
-        dispatch_sync(dispatch_get_main_queue(), [strongSelf]() {
-            [strongSelf allocateVolumeView];
-        });
+    if (pthread_main_np()) {
+        [self setVolumeView:adoptNS([allocMPVolumeViewInstance() init])];
         return;
     }
 
-    _volumeView = adoptNS([allocMPVolumeViewInstance() init]);
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wirelessRoutesAvailableDidChange:) name:MPVolumeViewWirelessRoutesAvailableDidChangeNotification object:_volumeView.get()];
-    
+    RetainPtr<WebMediaSessionHelper> strongSelf = self;
+    dispatch_async(dispatch_get_main_queue(), [strongSelf]() {
+        RetainPtr<MPVolumeView> volumeView = adoptNS([allocMPVolumeViewInstance() init]);
+        callOnWebThreadOrDispatchAsyncOnMainThread([strongSelf, volumeView]() {
+            [strongSelf setVolumeView:volumeView];
+        });
+    });
+}
+
+- (void)setVolumeView:(RetainPtr<MPVolumeView>)volumeView
+{
+    if (_volumeView)
+        [[NSNotificationCenter defaultCenter] removeObserver:self name:MPVolumeViewWirelessRoutesAvailableDidChangeNotification object:_volumeView.get()];
+
+    _volumeView = volumeView;
+
+    if (_volumeView)
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wirelessRoutesAvailableDidChange:) name:MPVolumeViewWirelessRoutesAvailableDidChangeNotification object:_volumeView.get()];
 }
 
 - (id)initWithCallback:(MediaSessionManageriOS*)callback
@@ -321,7 +332,7 @@ void MediaSessionManageriOS::externalOutputDeviceAvailableDidChange()
 - (BOOL)hasWirelessTargetsAvailable
 {
     LOG(Media, "-[WebMediaSessionHelper hasWirelessTargetsAvailable]");
-    return [_volumeView areWirelessRoutesAvailable];
+    return _volumeView ? [_volumeView areWirelessRoutesAvailable] : NO;
 }
 
 - (void)startMonitoringAirPlayRoutes