Diff
Modified: trunk/LayoutTests/ChangeLog (245946 => 245947)
--- trunk/LayoutTests/ChangeLog 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/LayoutTests/ChangeLog 2019-05-31 04:46:39 UTC (rev 245947)
@@ -1,3 +1,14 @@
+2019-05-30 Jer Noble <jer.no...@apple.com>
+
+ Video playback in Safari should continue when CarPlay is plugged in
+ https://bugs.webkit.org/show_bug.cgi?id=198345
+ <rdar://problem/45505750>
+
+ Reviewed by Eric Carlson.
+
+ * media/video-isplayingtoautomotiveheadunit-expected.txt: Added.
+ * media/video-isplayingtoautomotiveheadunit.html: Added.
+
2019-05-30 Youenn Fablet <you...@apple.com>
Add an option to mute audio capture automatically when page is not visible
Added: trunk/LayoutTests/media/video-isplayingtoautomotiveheadunit-expected.txt (0 => 245947)
--- trunk/LayoutTests/media/video-isplayingtoautomotiveheadunit-expected.txt (rev 0)
+++ trunk/LayoutTests/media/video-isplayingtoautomotiveheadunit-expected.txt 2019-05-31 04:46:39 UTC (rev 245947)
@@ -0,0 +1,15 @@
+
+RUN(video.src = "" "content/test"))
+EVENT(canplaythrough)
+RUN(video.play())
+EVENT(playing)
+RUN(internals.setMediaSessionRestrictions("videoaudio", "suspendedunderlockplaybackrestricted"))
+RUN(internals.applicationDidEnterBackground(true))
+EVENT(pause)
+RUN(internals.applicationWillEnterForeground(true))
+EVENT(playing)
+RUN(internals.setIsPlayingToAutomotiveHeadUnit(true))
+RUN(internals.applicationDidEnterBackground(true))
+EXPECTED (video.paused == 'false') OK
+END OF TEST
+
Added: trunk/LayoutTests/media/video-isplayingtoautomotiveheadunit.html (0 => 245947)
--- trunk/LayoutTests/media/video-isplayingtoautomotiveheadunit.html (rev 0)
+++ trunk/LayoutTests/media/video-isplayingtoautomotiveheadunit.html 2019-05-31 04:46:39 UTC (rev 245947)
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>video-isplayingtoautomotiveheadunit</title>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+
+ run('video.src = "" "content/test")');
+ await waitFor(video, 'canplaythrough');
+
+ runWithKeyDown('video.play()');
+ await waitFor(video, 'playing');
+
+ run('internals.setMediaSessionRestrictions("videoaudio", "suspendedunderlockplaybackrestricted")')
+ run('internals.applicationDidEnterBackground(true)');
+ await waitFor(video, 'pause');
+
+ run('internals.applicationWillEnterForeground(true)');
+ await waitFor(video, 'playing');
+
+ run('internals.setIsPlayingToAutomotiveHeadUnit(true)');
+ run('internals.applicationDidEnterBackground(true)');
+ testExpected('video.paused', false);
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <video controls></video>
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (245946 => 245947)
--- trunk/Source/WebCore/ChangeLog 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/ChangeLog 2019-05-31 04:46:39 UTC (rev 245947)
@@ -1,3 +1,40 @@
+2019-05-30 Jer Noble <jer.no...@apple.com>
+
+ Video playback in Safari should continue when CarPlay is plugged in
+ https://bugs.webkit.org/show_bug.cgi?id=198345
+ <rdar://problem/45505750>
+
+ Reviewed by Eric Carlson.
+
+ Test: media/video-isplayingtoautomotiveheadunit.html
+
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::shouldOverrideBackgroundPlaybackRestriction const):
+ * platform/audio/PlatformMediaSessionManager.cpp:
+ (WebCore::PlatformMediaSessionManager::setIsPlayingToAutomotiveHeadUnit):
+ * platform/audio/PlatformMediaSessionManager.h:
+ (WebCore::PlatformMediaSessionManager::isPlayingToAutomotiveHeadUnit const):
+ * platform/audio/ios/MediaSessionManagerIOS.h:
+ * platform/audio/ios/MediaSessionManagerIOS.mm:
+ (WebCore::MediaSessionManageriOS::MediaSessionManageriOS):
+ (WebCore::MediaSessionManageriOS::carPlayServerDied):
+ (WebCore::MediaSessionManageriOS::updateCarPlayIsConnected):
+ (-[WebMediaSessionHelper initWithCallback:]):
+ (-[WebMediaSessionHelper startMonitoringAirPlayRoutes]):
+ (-[WebMediaSessionHelper interruption:]):
+ (-[WebMediaSessionHelper applicationWillEnterForeground:]):
+ (-[WebMediaSessionHelper applicationDidBecomeActive:]):
+ (-[WebMediaSessionHelper applicationWillResignActive:]):
+ (-[WebMediaSessionHelper wirelessRoutesAvailableDidChange:]):
+ (-[WebMediaSessionHelper applicationDidEnterBackground:]):
+ (-[WebMediaSessionHelper carPlayServerDied:]):
+ (-[WebMediaSessionHelper carPlayIsConnectedDidChange:]):
+ * testing/Internals.cpp:
+ (WebCore::Internals::resetToConsistentState):
+ (WebCore::Internals::setIsPlayingToAutomotiveHeadUnit):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2019-05-30 Youenn Fablet <you...@apple.com>
Add an option to mute audio capture automatically when page is not visible
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (245946 => 245947)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2019-05-31 04:46:39 UTC (rev 245947)
@@ -7715,6 +7715,10 @@
INFO_LOG(LOGIDENTIFIER, "returning true because isPlayingToExternalTarget() is true");
return true;
}
+ if (PlatformMediaSessionManager::sharedManager().isPlayingToAutomotiveHeadUnit()) {
+ INFO_LOG(LOGIDENTIFIER, "returning true because isPlayingToAutomotiveHeadUnit() is true");
+ return true;
+ }
if (m_videoFullscreenMode & VideoFullscreenModePictureInPicture)
return true;
#if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
@@ -7726,6 +7730,10 @@
INFO_LOG(LOGIDENTIFIER, "returning true because isPlayingToExternalTarget() is true");
return true;
}
+ if (PlatformMediaSessionManager::sharedManager().isPlayingToAutomotiveHeadUnit()) {
+ INFO_LOG(LOGIDENTIFIER, "returning true because isPlayingToAutomotiveHeadUnit() is true");
+ return true;
+ }
}
return false;
}
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp (245946 => 245947)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp 2019-05-31 04:46:39 UTC (rev 245947)
@@ -391,7 +391,15 @@
#endif
}
+void PlatformMediaSessionManager::setIsPlayingToAutomotiveHeadUnit(bool isPlayingToAutomotiveHeadUnit)
+{
+ if (isPlayingToAutomotiveHeadUnit == m_isPlayingToAutomotiveHeadUnit)
+ return;
+ ALWAYS_LOG(LOGIDENTIFIER, isPlayingToAutomotiveHeadUnit);
+ m_isPlayingToAutomotiveHeadUnit = isPlayingToAutomotiveHeadUnit;
+}
+
void PlatformMediaSessionManager::sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession& session)
{
if (!m_isApplicationInBackground || !(m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted))
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h (245946 => 245947)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2019-05-31 04:46:39 UTC (rev 245947)
@@ -129,6 +129,9 @@
void sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession&);
+ WEBCORE_EXPORT void setIsPlayingToAutomotiveHeadUnit(bool);
+ bool isPlayingToAutomotiveHeadUnit() const { return m_isPlayingToAutomotiveHeadUnit; }
+
void forEachMatchingSession(const Function<bool(const PlatformMediaSession&)>& predicate, const Function<void(PlatformMediaSession&)>& matchingCallback);
protected:
@@ -188,6 +191,7 @@
mutable bool m_isApplicationInBackground { false };
bool m_willIgnoreSystemInterruptions { false };
bool m_processIsSuspended { false };
+ bool m_isPlayingToAutomotiveHeadUnit { false };
#if USE(AUDIO_SESSION)
bool m_becameActive { false };
Modified: trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h (245946 => 245947)
--- trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h 2019-05-31 04:46:39 UTC (rev 245947)
@@ -47,6 +47,10 @@
void externalOutputDeviceAvailableDidChange();
bool hasWirelessTargetsAvailable() override;
+#if HAVE(CELESTIAL)
+ void carPlayServerDied();
+ void updateCarPlayIsConnected(Optional<bool>&&);
+#endif
private:
friend class PlatformMediaSessionManager;
Modified: trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm (245946 => 245947)
--- trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm 2019-05-31 04:46:39 UTC (rev 245947)
@@ -56,6 +56,10 @@
SOFT_LINK_PRIVATE_FRAMEWORK_OPTIONAL(Celestial)
SOFT_LINK_CLASS_OPTIONAL(Celestial, AVSystemController)
SOFT_LINK_CONSTANT_MAY_FAIL(Celestial, AVSystemController_PIDToInheritApplicationStateFrom, NSString *)
+SOFT_LINK_CONSTANT_MAY_FAIL(Celestial, AVSystemController_CarPlayIsConnectedAttribute, NSString *)
+SOFT_LINK_CONSTANT_MAY_FAIL(Celestial, AVSystemController_CarPlayIsConnectedDidChangeNotification, NSString *)
+SOFT_LINK_CONSTANT_MAY_FAIL(Celestial, AVSystemController_CarPlayIsConnectedNotificationParameter, NSString *)
+SOFT_LINK_CONSTANT_MAY_FAIL(Celestial, AVSystemController_ServerConnectionDiedNotification, NSString *)
#endif
using namespace WebCore;
@@ -109,6 +113,10 @@
m_objcObserver = adoptNS([[WebMediaSessionHelper alloc] initWithCallback:this]);
END_BLOCK_OBJC_EXCEPTIONS
resetRestrictions();
+
+#if HAVE(CELESTIAL)
+ updateCarPlayIsConnected(WTF::nullopt);
+#endif
}
MediaSessionManageriOS::~MediaSessionManageriOS()
@@ -194,6 +202,29 @@
END_BLOCK_OBJC_EXCEPTIONS
}
+#if HAVE(CELESTIAL)
+void MediaSessionManageriOS::carPlayServerDied()
+{
+ ALWAYS_LOG(LOGIDENTIFIER);
+ updateCarPlayIsConnected(WTF::nullopt);
+}
+
+void MediaSessionManageriOS::updateCarPlayIsConnected(Optional<bool>&& carPlayIsConnected)
+{
+ if (carPlayIsConnected) {
+ setIsPlayingToAutomotiveHeadUnit(carPlayIsConnected.value());
+ return;
+ }
+
+ if (!canLoadAVSystemController_CarPlayIsConnectedAttribute()) {
+ setIsPlayingToAutomotiveHeadUnit(false);
+ return;
+ }
+
+ setIsPlayingToAutomotiveHeadUnit([[[getAVSystemControllerClass() sharedAVSystemController] attributeForKey:getAVSystemController_CarPlayIsConnectedAttribute()] boolValue]);
+}
+#endif
+
} // namespace WebCore
@implementation WebMediaSessionHelper
@@ -218,6 +249,12 @@
[center addObserver:self selector:@selector(applicationWillResignActive:) name:WebUIApplicationWillResignActiveNotification object:nil];
[center addObserver:self selector:@selector(applicationDidEnterBackground:) name:PAL::get_UIKit_UIApplicationDidEnterBackgroundNotification() object:nil];
[center addObserver:self selector:@selector(applicationDidEnterBackground:) name:WebUIApplicationDidEnterBackgroundNotification object:nil];
+#if HAVE(CELESTIAL)
+ if (canLoadAVSystemController_ServerConnectionDiedNotification())
+ [center addObserver:self selector:@selector(carPlayServerDied:) name:getAVSystemController_ServerConnectionDiedNotification() object:nil];
+ if (canLoadAVSystemController_CarPlayIsConnectedDidChangeNotification())
+ [center addObserver:self selector:@selector(carPlayIsConnectedDidChange:) name:getAVSystemController_CarPlayIsConnectedDidChangeNotification() object:nil];
+#endif
// Now playing won't work unless we turn on the delivery of remote control events.
dispatch_async(dispatch_get_main_queue(), ^ {
@@ -286,7 +323,7 @@
LOG(Media, "-[WebMediaSessionHelper startMonitoringAirPlayRoutes]");
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self)]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self)]() mutable {
ASSERT(!protectedSelf->_routeDetector);
if (protectedSelf->_callback) {
@@ -293,7 +330,7 @@
BEGIN_BLOCK_OBJC_EXCEPTIONS
protectedSelf->_routeDetector = adoptNS([PAL::allocAVRouteDetectorInstance() init]);
protectedSelf->_routeDetector.get().routeDetectionEnabled = protectedSelf->_monitoringAirPlayRoutes;
- [[NSNotificationCenter defaultCenter] addObserver:protectedSelf selector:@selector(wirelessRoutesAvailableDidChange:) name:AVRouteDetectorMultipleRoutesDetectedDidChangeNotification object:protectedSelf->_routeDetector.get()];
+ [[NSNotificationCenter defaultCenter] addObserver:protectedSelf.get() selector:@selector(wirelessRoutesAvailableDidChange:) name:AVRouteDetectorMultipleRoutesDetectedDidChangeNotification object:protectedSelf->_routeDetector.get()];
protectedSelf->_callback->externalOutputDeviceAvailableDidChange();
END_BLOCK_OBJC_EXCEPTIONS
@@ -328,7 +365,7 @@
if (type == AVAudioSessionInterruptionTypeEnded && [[[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey] unsignedIntegerValue] == AVAudioSessionInterruptionOptionShouldResume)
flags = PlatformMediaSession::MayResumePlaying;
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self), type, flags]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self), type, flags]() mutable {
auto* callback = protectedSelf->_callback;
if (!callback)
return;
@@ -351,7 +388,7 @@
LOG(Media, "-[WebMediaSessionHelper applicationWillEnterForeground]");
BOOL isSuspendedUnderLock = [[[notification userInfo] objectForKey:@"isSuspendedUnderLock"] boolValue];
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self), isSuspendedUnderLock]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self), isSuspendedUnderLock]() mutable {
if (auto* callback = protectedSelf->_callback)
callback->applicationWillEnterForeground(isSuspendedUnderLock);
});
@@ -366,7 +403,7 @@
LOG(Media, "-[WebMediaSessionHelper applicationDidBecomeActive]");
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self)]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self)]() mutable {
if (auto* callback = protectedSelf->_callback)
callback->applicationDidBecomeActive();
});
@@ -381,7 +418,7 @@
LOG(Media, "-[WebMediaSessionHelper applicationWillResignActive]");
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self)]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self)]() mutable {
if (auto* callback = protectedSelf->_callback)
callback->applicationWillBecomeInactive();
});
@@ -396,7 +433,7 @@
LOG(Media, "-[WebMediaSessionHelper wirelessRoutesAvailableDidChange]");
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self)]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self)]() mutable {
if (auto* callback = protectedSelf->_callback)
callback->externalOutputDeviceAvailableDidChange();
});
@@ -410,11 +447,44 @@
LOG(Media, "-[WebMediaSessionHelper applicationDidEnterBackground]");
BOOL isSuspendedUnderLock = [[[notification userInfo] objectForKey:@"isSuspendedUnderLock"] boolValue];
- callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = WTFMove(self), isSuspendedUnderLock]() mutable {
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self), isSuspendedUnderLock]() mutable {
if (auto* callback = protectedSelf->_callback)
callback->applicationDidEnterBackground(isSuspendedUnderLock);
});
}
+
+#if HAVE(CELESTIAL)
+- (void)carPlayServerDied:(NSNotification *)notification
+{
+ if (!_callback)
+ return;
+
+ LOG(Media, "-[WebMediaSessionHelper carPlayServerDied:]");
+ UNUSED_PARAM(notification);
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self)]() mutable {
+ if (auto* callback = protectedSelf->_callback)
+ callback->carPlayServerDied();
+ });
+}
+
+- (void)carPlayIsConnectedDidChange:(NSNotification *)notification
+{
+ if (!_callback)
+ return;
+
+ Optional<bool> carPlayIsConnected;
+ if (notification && canLoadAVSystemController_CarPlayIsConnectedNotificationParameter()) {
+ NSNumber *nsCarPlayIsConnected = [[notification userInfo] valueForKey:getAVSystemController_CarPlayIsConnectedNotificationParameter()];
+ if (nsCarPlayIsConnected)
+ carPlayIsConnected = [nsCarPlayIsConnected boolValue];
+ }
+
+ callOnWebThreadOrDispatchAsyncOnMainThread([protectedSelf = retainPtr(self), carPlayIsConnected = WTFMove(carPlayIsConnected)]() mutable {
+ if (auto* callback = protectedSelf->_callback)
+ callback->updateCarPlayIsConnected(WTFMove(carPlayIsConnected));
+ });
+}
+#endif // HAVE(CELESTIAL)
@end
#endif // PLATFORM(IOS_FAMILY)
Modified: trunk/Source/WebCore/testing/Internals.cpp (245946 => 245947)
--- trunk/Source/WebCore/testing/Internals.cpp 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/testing/Internals.cpp 2019-05-31 04:46:39 UTC (rev 245947)
@@ -492,6 +492,7 @@
PlatformMediaSessionManager::sharedManager().resetRestrictions();
PlatformMediaSessionManager::sharedManager().setWillIgnoreSystemInterruptions(true);
#endif
+ PlatformMediaSessionManager::sharedManager().setIsPlayingToAutomotiveHeadUnit(false);
#if HAVE(ACCESSIBILITY)
AXObjectCache::setEnhancedUserInterfaceAccessibility(false);
AXObjectCache::disableAccessibility();
@@ -5075,4 +5076,9 @@
request.setMaximumIntervalForUserGestureForwarding(interval);
}
+void Internals::setIsPlayingToAutomotiveHeadUnit(bool isPlaying)
+{
+ PlatformMediaSessionManager::sharedManager().setIsPlayingToAutomotiveHeadUnit(isPlaying);
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/testing/Internals.h (245946 => 245947)
--- trunk/Source/WebCore/testing/Internals.h 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/testing/Internals.h 2019-05-31 04:46:39 UTC (rev 245947)
@@ -824,6 +824,8 @@
void setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest&, double);
+ void setIsPlayingToAutomotiveHeadUnit(bool);
+
private:
explicit Internals(Document&);
Document* contextDocument() const;
Modified: trunk/Source/WebCore/testing/Internals.idl (245946 => 245947)
--- trunk/Source/WebCore/testing/Internals.idl 2019-05-31 04:46:05 UTC (rev 245946)
+++ trunk/Source/WebCore/testing/Internals.idl 2019-05-31 04:46:39 UTC (rev 245947)
@@ -754,4 +754,6 @@
void testDictionaryLogging();
void setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest xhr, double interval);
+
+ void setIsPlayingToAutomotiveHeadUnit(boolean value);
};