[iOS] <video> elements without audio tracks should not interrupt music
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Nov 2015 18:48:47 +0000 (18:48 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Nov 2015 18:48:47 +0000 (18:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149888

Reviewed by Eric Carlson.

Source/WebCore:

Tests: TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm

Only set the AVAudioSession category to "playback" when the video element in question has an
audio track.

Add a new PlatformMediaSessionClient method called canProduceAudio(), overridden in HTMLMediaElement
and AudioContext, which is checked when updating the AudioSession category in
PlatformMediaSessionManager::updateSessionState().

* Modules/webaudio/AudioContext.h:
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::mediaPlayerCharacteristicChanged):
* html/HTMLMediaElement.h:
* platform/audio/PlatformMediaSession.cpp:
(WebCore::PlatformMediaSession::activeAudioSessionRequired):
(WebCore::PlatformMediaSession::setCanProduceAudio):
* platform/audio/PlatformMediaSession.h:
(WebCore::PlatformMediaSession::canProduceAudio):
* platform/audio/PlatformMediaSessionManager.cpp:
(WebCore::PlatformMediaSessionManager::canProduceAudio):
(WebCore::PlatformMediaSessionManager::sessionCanProduceAudioChanged):
(WebCore::PlatformMediaSessionManager::activeAudioSessionRequired):
(WebCore::PlatformMediaSessionManager::sessionWillBeginPlayback):
* platform/audio/PlatformMediaSessionManager.h:
* platform/audio/mac/MediaSessionManagerMac.cpp:
(PlatformMediaSessionManager::updateSessionState):

Tools:

Add tests to ensure that the AVAudioSession category is correctly set when playing
back media both with and without audio tracks.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm: Added.
(-[AudioSessionCategoryUIWebViewDelegate webView:shouldStartLoadWithRequest:navigationType:]):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKit/ios/video-with-audio.html: Added.
* TestWebKitAPI/Tests/WebKit/ios/video-with-audio.mp4: Added.
* TestWebKitAPI/Tests/WebKit/ios/video-without-audio.html: Added.
* TestWebKitAPI/Tests/WebKit/ios/video-without-audio.mp4: Added.

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

17 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/webaudio/AudioContext.cpp
Source/WebCore/Modules/webaudio/AudioContext.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/platform/audio/PlatformMediaSession.cpp
Source/WebCore/platform/audio/PlatformMediaSession.h
Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp
Source/WebCore/platform/audio/PlatformMediaSessionManager.h
Source/WebCore/platform/audio/mac/MediaSessionManagerMac.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.mp4 [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.mp4 [new file with mode: 0644]

index b3e8d15a5735edc9395052508f497f6e025e001f..fd4b148b10f57fb8c9c1642a82a778c8b549785b 100644 (file)
@@ -1,3 +1,37 @@
+2015-11-04  Jer Noble  <jer.noble@apple.com>
+
+        [iOS] <video> elements without audio tracks should not interrupt music
+        https://bugs.webkit.org/show_bug.cgi?id=149888
+
+        Reviewed by Eric Carlson.
+
+        Tests: TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm
+
+        Only set the AVAudioSession category to "playback" when the video element in question has an
+        audio track.
+
+        Add a new PlatformMediaSessionClient method called canProduceAudio(), overridden in HTMLMediaElement
+        and AudioContext, which is checked when updating the AudioSession category in 
+        PlatformMediaSessionManager::updateSessionState().
+
+        * Modules/webaudio/AudioContext.h:
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::mediaPlayerCharacteristicChanged):
+        * html/HTMLMediaElement.h:
+        * platform/audio/PlatformMediaSession.cpp:
+        (WebCore::PlatformMediaSession::activeAudioSessionRequired):
+        (WebCore::PlatformMediaSession::setCanProduceAudio):
+        * platform/audio/PlatformMediaSession.h:
+        (WebCore::PlatformMediaSession::canProduceAudio):
+        * platform/audio/PlatformMediaSessionManager.cpp:
+        (WebCore::PlatformMediaSessionManager::canProduceAudio):
+        (WebCore::PlatformMediaSessionManager::sessionCanProduceAudioChanged):
+        (WebCore::PlatformMediaSessionManager::activeAudioSessionRequired):
+        (WebCore::PlatformMediaSessionManager::sessionWillBeginPlayback):
+        * platform/audio/PlatformMediaSessionManager.h:
+        * platform/audio/mac/MediaSessionManagerMac.cpp:
+        (PlatformMediaSessionManager::updateSessionState):
+
 2015-11-03  Dean Jackson  <dino@apple.com>
 
         Accept 8 and 4 value hex colors (#RRGGBBAA)
index 10e421260bc922911c0da37c2fca9366c7abb768..40a40b19fa792102fcdb09439dd2c500be46c430 100644 (file)
@@ -181,6 +181,8 @@ void AudioContext::constructCommon()
 #if PLATFORM(COCOA)
     addBehaviorRestriction(RequirePageConsentForAudioStartRestriction);
 #endif
+
+    m_mediaSession->setCanProduceAudio(true);
 }
 
 AudioContext::~AudioContext()
index 92aa3485e9ddd5ccc8f33ab28fcee9af937ec2ca..37bac49fb9f5b700df26c14a9d1863985cf3343f 100644 (file)
@@ -316,12 +316,12 @@ private:
     void derefUnfinishedSourceNodes();
 
     // PlatformMediaSessionClient
-    virtual PlatformMediaSession::MediaType mediaType() const override { return PlatformMediaSession::WebAudio; }
-    virtual PlatformMediaSession::MediaType presentationType() const override { return PlatformMediaSession::WebAudio; }
-    virtual void mayResumePlayback(bool shouldResume) override;
-    virtual void suspendPlayback() override;
-    virtual bool canReceiveRemoteControlCommands() const override { return false; }
-    virtual void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType) override { }
+    PlatformMediaSession::MediaType mediaType() const override { return PlatformMediaSession::WebAudio; }
+    PlatformMediaSession::MediaType presentationType() const override { return PlatformMediaSession::WebAudio; }
+    void mayResumePlayback(bool shouldResume) override;
+    void suspendPlayback() override;
+    bool canReceiveRemoteControlCommands() const override { return false; }
+    void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType) override { }
     bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const override { return false; }
 
     // EventTarget
index fd9809dfe42eab2b3049879bb0a5c7de51b6a3d0..957a97eaa243784cbf5328cdee7130f8466e2d8a 100644 (file)
@@ -4561,6 +4561,8 @@ void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
     if (isPlaying() && !m_mediaSession->playbackPermitted(*this))
         pauseInternal();
 
+    m_mediaSession->setCanProduceAudio(m_player && m_readyState >= HAVE_METADATA && hasAudio());
+
 #if ENABLE(MEDIA_SESSION)
     document().updateIsPlayingMedia(m_elementID);
 #else
@@ -4939,6 +4941,8 @@ void HTMLMediaElement::clearMediaPlayer(int flags)
         configureTextTrackDisplay();
 #endif
 
+    m_mediaSession->setCanProduceAudio(false);
+
     updateSleepDisabling();
 }
 
index 5fa9dac8ffb323bacd57592961e8da1779124244..050acb46071a4fec54af1c6fe7f31f0ae1980593 100644 (file)
@@ -725,16 +725,16 @@ private:
 #endif
 
     // PlatformMediaSessionClient Overrides
-    virtual PlatformMediaSession::MediaType mediaType() const override;
-    virtual PlatformMediaSession::MediaType presentationType() const override;
-    virtual PlatformMediaSession::DisplayType displayType() const override;
-    virtual void suspendPlayback() override;
-    virtual void mayResumePlayback(bool shouldResume) override;
-    virtual String mediaSessionTitle() const override;
-    virtual double mediaSessionDuration() const override { return duration(); }
-    virtual double mediaSessionCurrentTime() const override { return currentTime(); }
-    virtual bool canReceiveRemoteControlCommands() const override { return true; }
-    virtual void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType) override;
+    PlatformMediaSession::MediaType mediaType() const override;
+    PlatformMediaSession::MediaType presentationType() const override;
+    PlatformMediaSession::DisplayType displayType() const override;
+    void suspendPlayback() override;
+    void mayResumePlayback(bool shouldResume) override;
+    String mediaSessionTitle() const override;
+    double mediaSessionDuration() const override { return duration(); }
+    double mediaSessionCurrentTime() const override { return currentTime(); }
+    bool canReceiveRemoteControlCommands() const override { return true; }
+    void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType) override;
     bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const override;
 
     virtual void pageMutedStateDidChange() override;
index 71ff19eac1d1b713d96c8d9ed8c4e25e58ce5f0a..afd948c0dff985aa28e264dbdea280f49a967a91 100644 (file)
@@ -257,6 +257,24 @@ PlatformMediaSession::DisplayType PlatformMediaSession::displayType() const
     return m_client.displayType();
 }
 
+bool PlatformMediaSession::activeAudioSessionRequired()
+{
+    if (mediaType() == PlatformMediaSession::None)
+        return false;
+    if (state() != PlatformMediaSession::State::Playing)
+        return false;
+    return m_canProduceAudio;
+}
+
+void PlatformMediaSession::setCanProduceAudio(bool canProduceAudio)
+{
+    if (m_canProduceAudio == canProduceAudio)
+        return;
+    m_canProduceAudio = canProduceAudio;
+
+    PlatformMediaSessionManager::sharedManager().sessionCanProduceAudioChanged(*this);
+}
+
 String PlatformMediaSessionClient::mediaSessionTitle() const
 {
     return String();
@@ -271,5 +289,6 @@ double PlatformMediaSessionClient::mediaSessionCurrentTime() const
 {
     return MediaPlayer::invalidTime();
 }
+
 }
 #endif
index a010ec6d26e299a0f20115f50823c340281cfe7d..13bcefd52644035a8ee0aefe1d05facd29932659 100644 (file)
@@ -133,6 +133,10 @@ public:
     virtual bool requiresPlaybackTargetRouteMonitoring() const { return false; }
 #endif
 
+    bool activeAudioSessionRequired();
+    bool canProduceAudio() const { return m_canProduceAudio; }
+    void setCanProduceAudio(bool);
+
 protected:
     PlatformMediaSessionClient& client() const { return m_client; }
 
@@ -147,6 +151,7 @@ private:
     int m_interruptionCount { 0 };
     bool m_notifyingClient;
     bool m_isPlayingToWirelessPlaybackTarget { false };
+    bool m_canProduceAudio { false };
 
     friend class PlatformMediaSessionManager;
 };
index 1f5fc7807f1cb5c6edc064c3bc6ff6ef8746c194..e3f62de8b07a931ccfe7cf8b33fc905760182848 100644 (file)
@@ -80,13 +80,23 @@ bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) cons
 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
 {
     for (auto* session : m_sessions) {
-        if (session->mediaType() != PlatformMediaSession::None && session->state() == PlatformMediaSession::State::Playing)
+        if (session->activeAudioSessionRequired())
             return true;
     }
     
     return false;
 }
 
+bool PlatformMediaSessionManager::canProduceAudio() const
+{
+    for (auto* session : m_sessions) {
+        if (session->canProduceAudio())
+            return true;
+    }
+
+    return false;
+}
+
 int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) const
 {
     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::WebAudio);
@@ -313,6 +323,11 @@ void PlatformMediaSessionManager::sessionIsPlayingToWirelessPlaybackTargetChange
         session.beginInterruption(PlatformMediaSession::EnteringBackground);
 }
 
+void PlatformMediaSessionManager::sessionCanProduceAudioChanged(PlatformMediaSession&)
+{
+    updateSessionState();
+}
+
 #if !PLATFORM(COCOA)
 void PlatformMediaSessionManager::updateSessionState()
 {
index 9471e0940e4a9c4e4cddc6e3bcb967e78bb0d8d5..ed0c36bc20a1e6f645f70006ea0ef001d30fea6a 100644 (file)
@@ -51,6 +51,7 @@ public:
     bool has(PlatformMediaSession::MediaType) const;
     int count(PlatformMediaSession::MediaType) const;
     bool activeAudioSessionRequired() const;
+    bool canProduceAudio() const;
 
     bool willIgnoreSystemInterruptions() const { return m_willIgnoreSystemInterruptions; }
     void setWillIgnoreSystemInterruptions(bool ignore) { m_willIgnoreSystemInterruptions = ignore; }
@@ -91,6 +92,7 @@ public:
     PlatformMediaSession* currentSession();
 
     void sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession&);
+    void sessionCanProduceAudioChanged(PlatformMediaSession&);
 
 protected:
     friend class PlatformMediaSession;
index b389eedfaee062494b39d13b4c7d38f141d64b67..7731a622eb23149653800755bc304133b8ca8f11 100644 (file)
@@ -60,8 +60,12 @@ void PlatformMediaSessionManager::updateSessionState()
     if (!Settings::shouldManageAudioSessionCategory())
         return;
 
-    if (has(PlatformMediaSession::Video) || has(PlatformMediaSession::Audio))
-        AudioSession::sharedSession().setCategory(AudioSession::MediaPlayback);
+    if (has(PlatformMediaSession::Video) || has(PlatformMediaSession::Audio)) {
+        if (canProduceAudio())
+            AudioSession::sharedSession().setCategory(AudioSession::MediaPlayback);
+        else
+            AudioSession::sharedSession().setCategory(AudioSession::AmbientSound);
+    }
     else if (has(PlatformMediaSession::WebAudio))
         AudioSession::sharedSession().setCategory(AudioSession::AmbientSound);
 #endif
index 54d5006bb88c08eece5a15cd1fe674b111ca0d46..a3c87cd9f336dadb9f592fb7dc784fc5ecb08ca4 100644 (file)
@@ -1,3 +1,22 @@
+2015-11-04  Jer Noble  <jer.noble@apple.com>
+
+        [iOS] <video> elements without audio tracks should not interrupt music
+        https://bugs.webkit.org/show_bug.cgi?id=149888
+
+        Reviewed by Eric Carlson.
+
+        Add tests to ensure that the AVAudioSession category is correctly set when playing
+        back media both with and without audio tracks.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm: Added.
+        (-[AudioSessionCategoryUIWebViewDelegate webView:shouldStartLoadWithRequest:navigationType:]):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKit/ios/video-with-audio.html: Added.
+        * TestWebKitAPI/Tests/WebKit/ios/video-with-audio.mp4: Added.
+        * TestWebKitAPI/Tests/WebKit/ios/video-without-audio.html: Added.
+        * TestWebKitAPI/Tests/WebKit/ios/video-without-audio.mp4: Added.
+
 2015-11-04  Alexey Proskuryakov  <ap@apple.com>
 
         iOS test results are not visible on the flakiness dashboard
index d185cf451f33e82f468ec838eea9160bf1522468..ed8378f1ef5c11d468a99fb6bf05cafffdad71f1 100644 (file)
                CD59F53519E9110D00CF1835 /* test-mse.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CD59F53319E910BC00CF1835 /* test-mse.mp4 */; };
                CDBFCC451A9FF45300A7B691 /* FullscreenZoomInitialFrame.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDBFCC431A9FF44800A7B691 /* FullscreenZoomInitialFrame.mm */; };
                CDBFCC461A9FF49E00A7B691 /* FullscreenZoomInitialFrame.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDBFCC421A9FF44800A7B691 /* FullscreenZoomInitialFrame.html */; };
+               CDC8E48D1BC5CB4500594FEC /* AudioSessionCategoryIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC8E4851BC5B19400594FEC /* AudioSessionCategoryIOS.mm */; };
+               CDC8E4941BC6F10800594FEC /* video-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E4891BC5C96200594FEC /* video-with-audio.html */; };
+               CDC8E4951BC6F10800594FEC /* video-with-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */; };
+               CDC8E4961BC6F10800594FEC /* video-without-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48B1BC5C96200594FEC /* video-without-audio.html */; };
+               CDC8E4971BC6F10800594FEC /* video-without-audio.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */; };
                CE14F1A4181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */; };
                CE3524F81B1431F60028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */; };
                CE3524F91B1441C40028A7C5 /* TextFieldDidBeginAndEndEditing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */; };
                        dstPath = TestWebKitAPI.resources;
                        dstSubfolderSpec = 7;
                        files = (
+                               CDC8E4941BC6F10800594FEC /* video-with-audio.html in Copy Resources */,
+                               CDC8E4951BC6F10800594FEC /* video-with-audio.mp4 in Copy Resources */,
+                               CDC8E4961BC6F10800594FEC /* video-without-audio.html in Copy Resources */,
+                               CDC8E4971BC6F10800594FEC /* video-without-audio.mp4 in Copy Resources */,
                                A1C4FB731BACD1CA003742D0 /* pages.pages in Copy Resources */,
                                51CD1C721B38D48400142CA5 /* modal-alerts-in-new-about-blank-window.html in Copy Resources */,
                                7AE9E5091AE5AE8B00CF874B /* test.pdf in Copy Resources */,
                CDBFCC421A9FF44800A7B691 /* FullscreenZoomInitialFrame.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = FullscreenZoomInitialFrame.html; sourceTree = "<group>"; };
                CDBFCC431A9FF44800A7B691 /* FullscreenZoomInitialFrame.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FullscreenZoomInitialFrame.mm; sourceTree = "<group>"; };
                CDC2C7141797089D00E627FB /* TimeRanges.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TimeRanges.cpp; sourceTree = "<group>"; };
+               CDC8E4851BC5B19400594FEC /* AudioSessionCategoryIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioSessionCategoryIOS.mm; sourceTree = "<group>"; };
+               CDC8E4891BC5C96200594FEC /* video-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "video-with-audio.html"; sourceTree = "<group>"; };
+               CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "video-with-audio.mp4"; sourceTree = "<group>"; };
+               CDC8E48B1BC5C96200594FEC /* video-without-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "video-without-audio.html"; sourceTree = "<group>"; };
+               CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "video-without-audio.mp4"; sourceTree = "<group>"; };
                CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = WillPerformClientRedirectToURLCrash.html; sourceTree = "<group>"; };
                CE32C7C718184C4900CD8C28 /* WillPerformClientRedirectToURLCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WillPerformClientRedirectToURLCrash.mm; sourceTree = "<group>"; };
                CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing.cpp; sourceTree = "<group>"; };
                                C07E6CAD13FD67650038B22B /* mac */,
                                C08587F913FEC39B001EF4E5 /* TestWebKitAPI */,
                                440A1D3614A01000008A66F2 /* WebCore */,
+                               CDC8E4981BC728AE00594FEC /* WebKit */,
                                BC9096411255616000083756 /* WebKit2 */,
                                1ABC3DEC1899BE55004F0626 /* WebKit2 Cocoa */,
                                BC3C4C6F14575B1D0025FB62 /* WebKit2 Objective-C */,
                        path = mac;
                        sourceTree = "<group>";
                };
+               CDC8E4981BC728AE00594FEC /* WebKit */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CDC8E4991BC728C000594FEC /* ios */,
+                       );
+                       path = WebKit;
+                       sourceTree = "<group>";
+               };
+               CDC8E4991BC728C000594FEC /* ios */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CDC8E49A1BC728FE00594FEC /* Resources */,
+                               CDC8E4851BC5B19400594FEC /* AudioSessionCategoryIOS.mm */,
+                       );
+                       path = ios;
+                       sourceTree = "<group>";
+               };
+               CDC8E49A1BC728FE00594FEC /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CDC8E4891BC5C96200594FEC /* video-with-audio.html */,
+                               CDC8E48A1BC5C96200594FEC /* video-with-audio.mp4 */,
+                               CDC8E48B1BC5C96200594FEC /* video-without-audio.html */,
+                               CDC8E48C1BC5C96200594FEC /* video-without-audio.mp4 */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
                FE217ECB1640A54A0052988B /* JavaScriptCore */ = {
                        isa = PBXGroup;
                        children = (
                                7CCE7EDA1A411A8700447C4C /* InstanceMethodSwizzler.mm in Sources */,
                                7C54A4BE1AA11CCA00380F78 /* WKBundleFileHandle.cpp in Sources */,
                                7CCE7F371A411B8E00447C4C /* IntegerToStringConversion.cpp in Sources */,
+                               CDC8E48D1BC5CB4500594FEC /* AudioSessionCategoryIOS.mm in Sources */,
                                7CCE7EAD1A411A3400447C4C /* JavaScriptTest.cpp in Sources */,
                                7CCE7EA51A411A0800447C4C /* JavaScriptTestMac.mm in Sources */,
                                7CCE7EC41A411A7E00447C4C /* JSWrapperForNodeInWebFrame.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm b/Tools/TestWebKitAPI/Tests/WebKit/ios/AudioSessionCategoryIOS.mm
new file mode 100644 (file)
index 0000000..2f82c6c
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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 PLATFORM(IOS)
+
+#import "PlatformUtilities.h"
+#import <AVFoundation/AVAudioSession.h>
+#import <WebCore/Settings.h>
+#import <UIKit/UIKit.h>
+#import <WebCore/SoftLinking.h>
+#import <WebKit/WebKitLegacy.h>
+#import <wtf/RetainPtr.h>
+
+SOFT_LINK_FRAMEWORK(UIKit)
+SOFT_LINK_CLASS(UIKit, UIWebView)
+SOFT_LINK_CLASS(UIKit, UIWindow)
+
+SOFT_LINK_FRAMEWORK(AVFoundation)
+SOFT_LINK_CLASS(AVFoundation, AVAudioSession)
+SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryAmbient, NSString *)
+SOFT_LINK_CONSTANT(AVFoundation, AVAudioSessionCategoryPlayback, NSString *)
+
+static bool didBeginPlaying = false;
+
+@interface AudioSessionCategoryUIWebViewDelegate : NSObject <UIWebViewDelegate>
+@end
+
+@implementation AudioSessionCategoryUIWebViewDelegate
+- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
+{
+    if ([request.URL.scheme isEqualToString:@"callback"] && [request.URL.resourceSpecifier isEqualToString:@"playing"]) {
+        didBeginPlaying = true;
+        return NO;
+    }
+
+    return YES;
+}
+@end
+
+namespace TestWebKitAPI {
+
+TEST(WebKit1, AudioSessionCategoryIOS)
+{
+    WebCore::Settings::setShouldManageAudioSessionCategory(true);
+    RetainPtr<UIWindow> uiWindow = adoptNS([[getUIWindowClass() alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    RetainPtr<UIWebView> uiWebView = adoptNS([[getUIWebViewClass() alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [uiWindow addSubview:uiWebView.get()];
+
+    uiWebView.get().mediaPlaybackRequiresUserAction = NO;
+    uiWebView.get().allowsInlineMediaPlayback = YES;
+
+    RetainPtr<AudioSessionCategoryUIWebViewDelegate> uiDelegate = adoptNS([[AudioSessionCategoryUIWebViewDelegate alloc] init]);
+    uiWebView.get().delegate = uiDelegate.get();
+
+    [uiWebView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"video-with-audio" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
+
+    Util::run(&didBeginPlaying);
+
+    EXPECT_WK_STREQ(getAVAudioSessionCategoryPlayback(), [[getAVAudioSessionClass() sharedInstance] category]);
+
+    didBeginPlaying = false;
+
+    [uiWebView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"video-without-audio" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
+
+    Util::run(&didBeginPlaying);
+
+    EXPECT_WK_STREQ(getAVAudioSessionCategoryAmbient(), [[getAVAudioSessionClass() sharedInstance] category]);
+}
+
+}
+
+#endif
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.html b/Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.html
new file mode 100644 (file)
index 0000000..d0d66bb
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script>
+    function go() {
+        var video = document.getElementsByTagName('video')[0];
+        video.addEventListener('playing', function() {
+            window.location = 'callback:playing';
+        });
+        video.play();
+    }
+   </script>
+</head>
+<body onload="go()">
+    <video src="video-with-audio.mp4" webkit-playsinline></video>
+</body>
+</html>
\ No newline at end of file
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.mp4 b/Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.mp4
new file mode 100644 (file)
index 0000000..bb15fd4
Binary files /dev/null and b/Tools/TestWebKitAPI/Tests/WebKit/ios/video-with-audio.mp4 differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.html b/Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.html
new file mode 100644 (file)
index 0000000..384c963
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script>
+    function go() {
+        var video = document.getElementsByTagName('video')[0];
+        video.addEventListener('playing', function() {
+            window.location = 'callback:playing';
+        });
+        video.play();
+    }
+    </script>
+</head>
+<body onload="go()">
+    <video src="video-without-audio.mp4" webkit-playsinline></video>
+</body>
+</html>
\ No newline at end of file
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.mp4 b/Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.mp4
new file mode 100644 (file)
index 0000000..ed028b5
Binary files /dev/null and b/Tools/TestWebKitAPI/Tests/WebKit/ios/video-without-audio.mp4 differ