[Mac] Audio tracks tagged as 'describes-video' are not automatically selected when...
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 May 2015 23:15:37 +0000 (23:15 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 May 2015 23:15:37 +0000 (23:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=145228

Reviewed by Eric Carlson.

Source/WebCore:

Tests: http/tests/media/hls/hls-accessiblity-describes-video.html
       media/accessiblity-describes-video.html

Add support to CaptionUserPreferences to return the user's preferred audio characteristics,
including "public.accessibility.describes-video". When the media accessibility options change,
trigger the HTMLMediaElement to update the list of tracks, and chose a new audio track if
appropriate. Manually filter those tracks matching the requested characteristics in
MediaSelectionGroupAVFObjC.  Allow these characteristics to be overrided by Internals for
testing purposes.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::captionPreferencesChanged): Call tracksChanged().
(WebCore::HTMLMediaElement::mediaPlayerPreferredAudioCharacteristics): Pass through to the page's caption preferences.
* html/HTMLMediaElement.h:
* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::setPreferredAudioCharacteristic): Simple setter.
(WebCore::CaptionUserPreferences::preferredAudioCharacteristics): Simple getter.
* page/CaptionUserPreferences.h:
* page/CaptionUserPreferencesMediaAF.cpp:
(WebCore::CaptionUserPreferencesMediaAF::~CaptionUserPreferencesMediaAF): Unregister for audio characteristics change notifications.
(WebCore::CaptionUserPreferencesMediaAF::setInterestedInCaptionPreferenceChanges): Register for same.
(WebCore::CaptionUserPreferencesMediaAF::setPreferredAudioCharacteristic): If in testing mode, pass to superclass; otherwise no-op.
(WebCore::CaptionUserPreferencesMediaAF::preferredAudioCharacteristics): If in testing mode, pass to superclass;
    otherwise, ask the media accessibility framework.
* page/CaptionUserPreferencesMediaAF.h:
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::tracksChanged): Pass through to m_private.
(WebCore::MediaPlayer::preferredAudioCharacteristics): Pass through to HTMLMediaElement.
* platform/graphics/MediaPlayer.h:
(WebCore::MediaPlayerClient::mediaPlayerPreferredAudioCharacteristics): Added; return empty vector by default.
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::tracksChanged): Added; no-op by default.
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
* platform/graphics/avfoundation/MediaSelectionGroupAVFObjC.h:
* platform/graphics/avfoundation/MediaSelectionGroupAVFObjC.mm:
(WebCore::MediaSelectionGroupAVFObjC::create): Added characteristics parameter.
(WebCore::MediaSelectionGroupAVFObjC::MediaSelectionGroupAVFObjC): Ditto.
(WebCore::MediaSelectionGroupAVFObjC::updateOptions): Add an additional filter against the
    passed-in characteristics.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::determineChangedTracksFromNewTracksAndOldItems): Pass the characteristics to the media selection group.
(WebCore::MediaPlayerPrivateAVFoundationObjC::updateAudioTracks): Pass in the user's preferred characteristics.
(WebCore::MediaPlayerPrivateAVFoundationObjC::updateVideoTracks): Pass in an empty vector.
* testing/Internals.cpp:
(WebCore::Internals::userPreferredAudioCharacteristics): Added; return the current setting.
(WebCore::Internals::setUserPreferredAudioCharacteristic): Added; pass through to CaptionUserPreferences.
* testing/Internals.h:
* testing/Internals.idl:

Add soft link macros which account for a possible failure to look up constants.

* platform/cf/MediaAccessibilitySoftLink.cpp:
* platform/cf/MediaAccessibilitySoftLink.h:
* platform/mac/SoftLinking.h:
* platform/win/SoftLinking.h:

LayoutTests:

* http/tests/media/hls/hls-accessiblity-describes-video-expected.txt: Added.
* http/tests/media/hls/hls-accessiblity-describes-video.html: Added.
* http/tests/media/resources/hls/audio-describes-video.m3u8: Added.
* http/tests/media/resources/hls/english/description.aac: Added.
* http/tests/media/resources/hls/english/description.m3u8: Added.
* media/accessiblity-describes-video-expected.txt: Added.
* media/accessiblity-describes-video.html: Added.
* media/content/audio-describes-video.mp4: Added.
* platform/mac-mavericks/TestExpectations:

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

32 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/media/hls/hls-accessiblity-describes-video-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/media/hls/hls-accessiblity-describes-video.html [new file with mode: 0644]
LayoutTests/http/tests/media/resources/hls/audio-describes-video.m3u8 [new file with mode: 0644]
LayoutTests/http/tests/media/resources/hls/english/description.aac [new file with mode: 0644]
LayoutTests/http/tests/media/resources/hls/english/description.m3u8 [new file with mode: 0644]
LayoutTests/media/accessiblity-describes-video-expected.txt [new file with mode: 0644]
LayoutTests/media/accessiblity-describes-video.html [new file with mode: 0644]
LayoutTests/media/content/audio-describes-video.mp4 [new file with mode: 0644]
LayoutTests/platform/mac-mavericks/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/page/CaptionUserPreferences.cpp
Source/WebCore/page/CaptionUserPreferences.h
Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp
Source/WebCore/page/CaptionUserPreferencesMediaAF.h
Source/WebCore/platform/cf/MediaAccessibilitySoftLink.cpp
Source/WebCore/platform/cf/MediaAccessibilitySoftLink.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h
Source/WebCore/platform/graphics/avfoundation/MediaSelectionGroupAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/MediaSelectionGroupAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
Source/WebCore/platform/mac/SoftLinking.h
Source/WebCore/platform/win/SoftLinking.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 6465a6e..96f8728 100644 (file)
@@ -1,3 +1,20 @@
+2015-05-22  Jer Noble  <jer.noble@apple.com>
+
+        [Mac] Audio tracks tagged as 'describes-video' are not automatically selected when that system accessibility option is set.
+        https://bugs.webkit.org/show_bug.cgi?id=145228
+
+        Reviewed by Eric Carlson.
+
+        * http/tests/media/hls/hls-accessiblity-describes-video-expected.txt: Added.
+        * http/tests/media/hls/hls-accessiblity-describes-video.html: Added.
+        * http/tests/media/resources/hls/audio-describes-video.m3u8: Added.
+        * http/tests/media/resources/hls/english/description.aac: Added.
+        * http/tests/media/resources/hls/english/description.m3u8: Added.
+        * media/accessiblity-describes-video-expected.txt: Added.
+        * media/accessiblity-describes-video.html: Added.
+        * media/content/audio-describes-video.mp4: Added.
+        * platform/mac-mavericks/TestExpectations:
+
 2015-05-22  Basile Clement  <basile_clement@apple.com>
 
         Allow DFGClobberize to return non-node constants that must be later created
diff --git a/LayoutTests/http/tests/media/hls/hls-accessiblity-describes-video-expected.txt b/LayoutTests/http/tests/media/hls/hls-accessiblity-describes-video-expected.txt
new file mode 100644 (file)
index 0000000..7949caf
--- /dev/null
@@ -0,0 +1,7 @@
+
+EVENT(canplaythrough)
+EXPECTED (video.audioTracks.length == '2') OK
+EXPECTED (video.audioTracks[0].enabled == 'false') OK
+EXPECTED (video.audioTracks[1].enabled == 'true') OK
+END OF TEST
+
diff --git a/LayoutTests/http/tests/media/hls/hls-accessiblity-describes-video.html b/LayoutTests/http/tests/media/hls/hls-accessiblity-describes-video.html
new file mode 100644 (file)
index 0000000..7804ea2
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=../../media-resources/video-test.js></script>
+        <script>
+            if (window.testRunner) {
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+            }
+
+            function start() {
+                if (window.internals)
+                    internals.setUserPreferredAudioCharacteristic('public.accessibility.describes-video');
+                video = document.getElementById('video');
+                waitForEvent('canplaythrough', canplaythrough);
+                video.src = '../resources/hls/audio-describes-video.m3u8';
+            }
+
+            function canplaythrough() {
+                testExpected('video.audioTracks.length', 2);
+                testExpected('video.audioTracks[0].enabled', false);
+                testExpected('video.audioTracks[1].enabled', true);
+                endTest();
+            }
+        </script>
+    </head>
+    <body onload="start()">
+        <video id="video"></video>
+    </body>
+</html>
diff --git a/LayoutTests/http/tests/media/resources/hls/audio-describes-video.m3u8 b/LayoutTests/http/tests/media/resources/hls/audio-describes-video.m3u8
new file mode 100644 (file)
index 0000000..9874833
--- /dev/null
@@ -0,0 +1,8 @@
+#EXTM3U
+
+#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="en-US",NAME="English Sound",AUTOSELECT=YES,DEFAULT=YES
+#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="en-US",NAME="English DVS",AUTOSELECT=YES,DEFAULT=NO,CHARACTERISTICS="public.accessibility.describes-video",URI="english/description.m3u8"
+
+#EXT-X-STREAM-INF:BANDWIDTH=634451,CODECS="mp4a.40.2, avc1.4d401e",RESOLUTION=640x480,AUDIO="bipbop_audio"
+bipbop/prog_index.m3u8
+#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=258613,CODECS="avc1.4d401e",URI="bipbop/iframe_index.m3u8"
diff --git a/LayoutTests/http/tests/media/resources/hls/english/description.aac b/LayoutTests/http/tests/media/resources/hls/english/description.aac
new file mode 100644 (file)
index 0000000..bb525a0
Binary files /dev/null and b/LayoutTests/http/tests/media/resources/hls/english/description.aac differ
diff --git a/LayoutTests/http/tests/media/resources/hls/english/description.m3u8 b/LayoutTests/http/tests/media/resources/hls/english/description.m3u8
new file mode 100644 (file)
index 0000000..820151a
--- /dev/null
@@ -0,0 +1,9 @@
+#EXTM3U
+#EXT-X-TARGETDURATION:6
+#EXT-X-VERSION:4
+#EXT-X-MEDIA-SEQUENCE:0
+#EXT-X-PLAYLIST-TYPE:VOD
+#EXTINF:6.40871,       
+#EXT-X-BYTERANGE:27949@0
+description.aac
+#EXT-X-ENDLIST
diff --git a/LayoutTests/media/accessiblity-describes-video-expected.txt b/LayoutTests/media/accessiblity-describes-video-expected.txt
new file mode 100644 (file)
index 0000000..7949caf
--- /dev/null
@@ -0,0 +1,7 @@
+
+EVENT(canplaythrough)
+EXPECTED (video.audioTracks.length == '2') OK
+EXPECTED (video.audioTracks[0].enabled == 'false') OK
+EXPECTED (video.audioTracks[1].enabled == 'true') OK
+END OF TEST
+
diff --git a/LayoutTests/media/accessiblity-describes-video.html b/LayoutTests/media/accessiblity-describes-video.html
new file mode 100644 (file)
index 0000000..962689e
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="video-test.js"></script>
+        <script src="media-file.js"></script>
+        <script>
+            if (window.testRunner) {
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+            }
+
+            function start() {
+                if (window.internals)
+                    internals.setUserPreferredAudioCharacteristic('public.accessibility.describes-video');
+                findMediaElement();
+                video = document.getElementById('video');
+                waitForEvent('canplaythrough', canplaythrough);
+                video.src = findMediaFile('video', 'content/audio-describes-video');
+            }
+
+            function canplaythrough() {
+                testExpected('video.audioTracks.length', 2);
+                testExpected('video.audioTracks[0].enabled', false);
+                testExpected('video.audioTracks[1].enabled', true);
+                endTest();
+            }
+        </script>
+    </head>
+    <body onload="start()">
+        <video id="video"></video>
+    </body>
+</html>
diff --git a/LayoutTests/media/content/audio-describes-video.mp4 b/LayoutTests/media/content/audio-describes-video.mp4
new file mode 100644 (file)
index 0000000..89ac10d
Binary files /dev/null and b/LayoutTests/media/content/audio-describes-video.mp4 differ
index 8b86a98..eb10419 100644 (file)
@@ -9,3 +9,6 @@ fast/events/mouse-force-up.html [ Skip ]
 # No support for Filters Level 2
 compositing/media-controls-bar-appearance.html [ Skip ]
 compositing/media-controls-bar-appearance-big.html [ Skip ]
+
+# No support for non-HLS Media Selection Group
+media/accessiblity-describes-video.html [ Skip ]
\ No newline at end of file
index a5d9943..9e7657b 100644 (file)
@@ -1,3 +1,67 @@
+2015-05-22  Jer Noble  <jer.noble@apple.com>
+
+        [Mac] Audio tracks tagged as 'describes-video' are not automatically selected when that system accessibility option is set.
+        https://bugs.webkit.org/show_bug.cgi?id=145228
+
+        Reviewed by Eric Carlson.
+
+        Tests: http/tests/media/hls/hls-accessiblity-describes-video.html
+               media/accessiblity-describes-video.html
+
+        Add support to CaptionUserPreferences to return the user's preferred audio characteristics,
+        including "public.accessibility.describes-video". When the media accessibility options change,
+        trigger the HTMLMediaElement to update the list of tracks, and chose a new audio track if
+        appropriate. Manually filter those tracks matching the requested characteristics in
+        MediaSelectionGroupAVFObjC.  Allow these characteristics to be overrided by Internals for
+        testing purposes.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::captionPreferencesChanged): Call tracksChanged().
+        (WebCore::HTMLMediaElement::mediaPlayerPreferredAudioCharacteristics): Pass through to the page's caption preferences.
+        * html/HTMLMediaElement.h:
+        * page/CaptionUserPreferences.cpp:
+        (WebCore::CaptionUserPreferences::setPreferredAudioCharacteristic): Simple setter.
+        (WebCore::CaptionUserPreferences::preferredAudioCharacteristics): Simple getter.
+        * page/CaptionUserPreferences.h:
+        * page/CaptionUserPreferencesMediaAF.cpp:
+        (WebCore::CaptionUserPreferencesMediaAF::~CaptionUserPreferencesMediaAF): Unregister for audio characteristics change notifications.
+        (WebCore::CaptionUserPreferencesMediaAF::setInterestedInCaptionPreferenceChanges): Register for same.
+        (WebCore::CaptionUserPreferencesMediaAF::setPreferredAudioCharacteristic): If in testing mode, pass to superclass; otherwise no-op.
+        (WebCore::CaptionUserPreferencesMediaAF::preferredAudioCharacteristics): If in testing mode, pass to superclass;
+            otherwise, ask the media accessibility framework.
+        * page/CaptionUserPreferencesMediaAF.h:
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::tracksChanged): Pass through to m_private.
+        (WebCore::MediaPlayer::preferredAudioCharacteristics): Pass through to HTMLMediaElement.
+        * platform/graphics/MediaPlayer.h:
+        (WebCore::MediaPlayerClient::mediaPlayerPreferredAudioCharacteristics): Added; return empty vector by default.
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::tracksChanged): Added; no-op by default.
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
+        * platform/graphics/avfoundation/MediaSelectionGroupAVFObjC.h:
+        * platform/graphics/avfoundation/MediaSelectionGroupAVFObjC.mm:
+        (WebCore::MediaSelectionGroupAVFObjC::create): Added characteristics parameter.
+        (WebCore::MediaSelectionGroupAVFObjC::MediaSelectionGroupAVFObjC): Ditto.
+        (WebCore::MediaSelectionGroupAVFObjC::updateOptions): Add an additional filter against the
+            passed-in characteristics.
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::determineChangedTracksFromNewTracksAndOldItems): Pass the characteristics to the media selection group.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::updateAudioTracks): Pass in the user's preferred characteristics.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::updateVideoTracks): Pass in an empty vector.
+        * testing/Internals.cpp:
+        (WebCore::Internals::userPreferredAudioCharacteristics): Added; return the current setting.
+        (WebCore::Internals::setUserPreferredAudioCharacteristic): Added; pass through to CaptionUserPreferences.
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
+        Add soft link macros which account for a possible failure to look up constants.
+
+        * platform/cf/MediaAccessibilitySoftLink.cpp:
+        * platform/cf/MediaAccessibilitySoftLink.h:
+        * platform/mac/SoftLinking.h:
+        * platform/win/SoftLinking.h:
+
 2015-05-22  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [Cocoa] Use CTFontDrawGlyphs() instead of CGContextShowGlyphsWithAdvances()/CGContextShowGlyphsAtPositions()
index 63ed425..0ecb7a4 100644 (file)
@@ -5482,6 +5482,9 @@ void HTMLMediaElement::captionPreferencesChanged()
         m_mediaControlsHost->updateCaptionDisplaySizes();
 #endif
 
+    if (m_player)
+        m_player->tracksChanged();
+
     if (!document().page())
         return;
 
@@ -5914,6 +5917,14 @@ String HTMLMediaElement::mediaPlayerSourceApplicationIdentifier() const
     return emptyString();
 }
 
+Vector<String> HTMLMediaElement::mediaPlayerPreferredAudioCharacteristics() const
+{
+    Page* page = document().page();
+    if (CaptionUserPreferences* captionPreferences = page ? page->group().captionPreferences() : nullptr)
+        return captionPreferences->preferredAudioCharacteristics();
+    return Vector<String>();
+}
+
 #if PLATFORM(IOS)
 String HTMLMediaElement::mediaPlayerNetworkInterfaceName() const
 {
index f9cdd86..d30b18f 100644 (file)
@@ -570,6 +570,7 @@ private:
     virtual bool mediaPlayerShouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&) override;
     virtual void mediaPlayerHandlePlaybackCommand(MediaSession::RemoteControlCommandType command) override { didReceiveRemoteControlCommand(command); }
     virtual String mediaPlayerSourceApplicationIdentifier() const override;
+    virtual Vector<String> mediaPlayerPreferredAudioCharacteristics() const override;
 
 #if PLATFORM(IOS)
     virtual String mediaPlayerNetworkInterfaceName() const override;
index d7280f8..d133b3e 100644 (file)
@@ -158,6 +158,20 @@ void CaptionUserPreferences::setPreferredLanguage(const String& language)
     notify();
 }
 
+void CaptionUserPreferences::setPreferredAudioCharacteristic(const String& characteristic)
+{
+    m_userPreferredAudioCharacteristic = characteristic;
+    notify();
+}
+
+Vector<String> CaptionUserPreferences::preferredAudioCharacteristics() const
+{
+    Vector<String> characteristics;
+    if (!m_userPreferredAudioCharacteristic.isEmpty())
+        characteristics.append(m_userPreferredAudioCharacteristic);
+    return characteristics;
+}
+
 static String trackDisplayName(TextTrack* track)
 {
     if (track == TextTrack::captionMenuOffItem())
index dfe6d49..43efa2e 100644 (file)
@@ -79,6 +79,9 @@ public:
     virtual void setPreferredLanguage(const String&);
     virtual Vector<String> preferredLanguages() const;
 
+    virtual void setPreferredAudioCharacteristic(const String&);
+    virtual Vector<String> preferredAudioCharacteristics() const;
+
     virtual String displayNameForTrack(TextTrack*) const;
     virtual Vector<RefPtr<TextTrack>> sortedTrackListForMenu(TextTrackList*);
 
@@ -104,6 +107,7 @@ private:
     CaptionDisplayMode m_displayMode;
     Timer m_timer;
     String m_userPreferredLanguage;
+    String m_userPreferredAudioCharacteristic;
     String m_captionsStyleSheetOverride;
     String m_primaryAudioTrackLanguageOverride;
     bool m_testingMode;
index 4011f2e..6a1e9b7 100644 (file)
@@ -106,6 +106,8 @@ CaptionUserPreferencesMediaAF::~CaptionUserPreferencesMediaAF()
 #if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
     if (kMAXCaptionAppearanceSettingsChangedNotification)
         CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, kMAXCaptionAppearanceSettingsChangedNotification, 0);
+    if (kMAAudibleMediaSettingsChangedNotification)
+        CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, kMAAudibleMediaSettingsChangedNotification, 0);
 #endif
 }
 
@@ -191,12 +193,16 @@ void CaptionUserPreferencesMediaAF::setInterestedInCaptionPreferenceChanges()
     if (!MediaAccessibilityLibrary())
         return;
 
-    if (!kMAXCaptionAppearanceSettingsChangedNotification)
+    if (!kMAXCaptionAppearanceSettingsChangedNotification && !canLoad_MediaAccessibility_kMAAudibleMediaSettingsChangedNotification())
         return;
 
     m_listeningForPreferenceChanges = true;
     m_registeringForNotification = true;
-    CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, userCaptionPreferencesChangedNotificationCallback, kMAXCaptionAppearanceSettingsChangedNotification, 0, CFNotificationSuspensionBehaviorCoalesce);
+
+    if (kMAXCaptionAppearanceSettingsChangedNotification)
+        CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, userCaptionPreferencesChangedNotificationCallback, kMAXCaptionAppearanceSettingsChangedNotification, 0, CFNotificationSuspensionBehaviorCoalesce);
+    if (canLoad_MediaAccessibility_kMAAudibleMediaSettingsChangedNotification())
+        CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, userCaptionPreferencesChangedNotificationCallback, kMAAudibleMediaSettingsChangedNotification, 0, CFNotificationSuspensionBehaviorCoalesce);
     m_registeringForNotification = false;
 
     // Generating and registering the caption stylesheet can be expensive and this method is called indirectly when the parser creates an audio or
@@ -466,6 +472,33 @@ Vector<String> CaptionUserPreferencesMediaAF::preferredLanguages() const
 
     return userPreferredLanguages;
 }
+
+void CaptionUserPreferencesMediaAF::setPreferredAudioCharacteristic(const String& characteristic)
+{
+    if (testingMode() || !MediaAccessibilityLibrary())
+        CaptionUserPreferences::setPreferredAudioCharacteristic(characteristic);
+}
+
+Vector<String> CaptionUserPreferencesMediaAF::preferredAudioCharacteristics() const
+{
+    if (testingMode() || !MediaAccessibilityLibrary() || !canLoad_MediaAccessibility_MAAudibleMediaCopyPreferredCharacteristics())
+        return CaptionUserPreferences::preferredAudioCharacteristics();
+
+    CFIndex characteristicCount = 0;
+    RetainPtr<CFArrayRef> characteristics = adoptCF(MAAudibleMediaCopyPreferredCharacteristics());
+    if (characteristics)
+        characteristicCount = CFArrayGetCount(characteristics.get());
+
+    if (!characteristicCount)
+        return CaptionUserPreferences::preferredAudioCharacteristics();
+
+    Vector<String> userPreferredAudioCharacteristics;
+    userPreferredAudioCharacteristics.reserveCapacity(characteristicCount);
+    for (CFIndex i = 0; i < characteristicCount; i++)
+        userPreferredAudioCharacteristics.append(static_cast<CFStringRef>(CFArrayGetValueAtIndex(characteristics.get(), i)));
+
+    return userPreferredAudioCharacteristics;
+}
 #endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
 
 String CaptionUserPreferencesMediaAF::captionsStyleSheetOverride() const
index 256ad95..c334cb1 100644 (file)
@@ -55,6 +55,9 @@ public:
     virtual void setPreferredLanguage(const String&) override;
     virtual Vector<String> preferredLanguages() const override;
 
+    virtual void setPreferredAudioCharacteristic(const String&) override;
+    virtual Vector<String> preferredAudioCharacteristics() const override;
+
     virtual void captionPreferencesChanged() override;
 
     bool shouldFilterTrackMenu() const { return true; }
index 2ac211f..11d4434 100644 (file)
@@ -47,7 +47,9 @@ SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, MediaAccessibility, MACaptionAppearanceGe
 SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, MediaAccessibility, MACaptionAppearanceAddSelectedLanguage, bool, (MACaptionAppearanceDomain domain, CFStringRef language), (domain, language));
 SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, MediaAccessibility, MACaptionAppearanceCopySelectedLanguages, CFArrayRef, (MACaptionAppearanceDomain domain), (domain));
 SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, MediaAccessibility, MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics,  CFArrayRef, (MACaptionAppearanceDomain domain), (domain));
+SOFT_LINK_FUNCTION_MAY_FAIL_FOR_SOURCE(WebCore, MediaAccessibility, MAAudibleMediaCopyPreferredCharacteristics, CFArrayRef, (), ());
 
 SOFT_LINK_CONSTANT_FOR_SOURCE(WebCore, MediaAccessibility, kMAXCaptionAppearanceSettingsChangedNotification, CFStringRef)
+SOFT_LINK_CONSTANT_MAY_FAIL_FOR_SOURCE(WebCore, MediaAccessibility, kMAAudibleMediaSettingsChangedNotification, CFStringRef)
 
 #endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
index ac8945c..c25d1da 100644 (file)
@@ -63,9 +63,13 @@ SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, MediaAccessibility, MACaptionAppearanceCo
 #define MACaptionAppearanceCopySelectedLanguages softLink_MediaAccessibility_MACaptionAppearanceCopySelectedLanguages
 SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, MediaAccessibility, MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics,  CFArrayRef, (MACaptionAppearanceDomain domain), (domain));
 #define MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics softLink_MediaAccessibility_MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics
+SOFT_LINK_FUNCTION_MAY_FAIL_FOR_HEADER(WebCore, MediaAccessibility, MAAudibleMediaCopyPreferredCharacteristics, CFArrayRef, (), ())
+#define MAAudibleMediaCopyPreferredCharacteristics softLink_MediaAccessibility_MAAudibleMediaCopyPreferredCharacteristics
 
 SOFT_LINK_CONSTANT_FOR_HEADER(WebCore, MediaAccessibility, kMAXCaptionAppearanceSettingsChangedNotification, CFStringRef)
 #define kMAXCaptionAppearanceSettingsChangedNotification get_MediaAccessibility_kMAXCaptionAppearanceSettingsChangedNotification()
+SOFT_LINK_CONSTANT_MAY_FAIL_FOR_HEADER(WebCore, MediaAccessibility, kMAAudibleMediaSettingsChangedNotification, CFStringRef)
+#define kMAAudibleMediaSettingsChangedNotification get_MediaAccessibility_kMAAudibleMediaSettingsChangedNotification()
 
 #endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
 
index e792acb..88de6e5 100644 (file)
@@ -1241,6 +1241,11 @@ void MediaPlayer::syncTextTrackBounds()
     m_private->syncTextTrackBounds();
 }
 
+void MediaPlayer::tracksChanged()
+{
+    m_private->tracksChanged();
+}
+
 #if ENABLE(AVF_CAPTIONS)
 void MediaPlayer::notifyTrackModeChanged()
 {
@@ -1357,6 +1362,11 @@ String MediaPlayer::sourceApplicationIdentifier() const
     return m_client.mediaPlayerSourceApplicationIdentifier();
 }
 
+Vector<String> MediaPlayer::preferredAudioCharacteristics() const
+{
+    return m_client.mediaPlayerPreferredAudioCharacteristics();
+}
+
 void MediaPlayerFactorySupport::callRegisterMediaEngine(MediaEngineRegister registerMediaEngine)
 {
     registerMediaEngine(addMediaEngine);
index 21dd870..8018998 100644 (file)
@@ -277,6 +277,7 @@ public:
 
     virtual double mediaPlayerRequestedPlaybackRate() const { return 0; }
     virtual MediaPlayerEnums::VideoFullscreenMode mediaPlayerFullscreenMode() const { return MediaPlayerEnums::VideoFullscreenModeNone; }
+    virtual Vector<String> mediaPlayerPreferredAudioCharacteristics() const { return Vector<String>(); }
 };
 
 class MediaPlayerSupportsTypeClient {
@@ -558,6 +559,7 @@ public:
     bool requiresTextTrackRepresentation() const;
     void setTextTrackRepresentation(TextTrackRepresentation*);
     void syncTextTrackBounds();
+    void tracksChanged();
 #if ENABLE(AVF_CAPTIONS)
     void notifyTrackModeChanged();
     Vector<RefPtr<PlatformTextTrack>> outOfBandTrackSources();
@@ -596,6 +598,7 @@ public:
     bool shouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&);
     void handlePlaybackCommand(MediaSession::RemoteControlCommandType);
     String sourceApplicationIdentifier() const;
+    Vector<String> preferredAudioCharacteristics() const;
 
 private:
     const MediaPlayerFactory* nextBestMediaEngine(const MediaPlayerFactory*) const;
index 6376201..41019c9 100644 (file)
@@ -238,6 +238,7 @@ public:
     virtual bool requiresTextTrackRepresentation() const { return false; }
     virtual void setTextTrackRepresentation(TextTrackRepresentation*) { }
     virtual void syncTextTrackBounds() { };
+    virtual void tracksChanged() { };
 #endif
 
 #if USE(PLATFORM_TEXT_TRACK_MENU)
index 683352f..19b0b3d 100644 (file)
@@ -241,7 +241,6 @@ protected:
     virtual MediaTime platformDuration() const = 0;
 
     virtual void beginLoadingMetadata() = 0;
-    virtual void tracksChanged() = 0;
     virtual void sizeChanged() = 0;
 
     virtual void createContextVideoRenderer() = 0;
index 276e0d5..9645337 100644 (file)
@@ -35,6 +35,7 @@
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
+#include <wtf/text/WTFString.h>
 
 OBJC_CLASS AVPlayerItem;
 OBJC_CLASS AVMediaSelectionGroup;
@@ -67,13 +68,13 @@ private:
 
 class MediaSelectionGroupAVFObjC : public RefCounted<MediaSelectionGroupAVFObjC> {
 public:
-    static PassRefPtr<MediaSelectionGroupAVFObjC> create(AVPlayerItem*, AVMediaSelectionGroup*);
+    static PassRefPtr<MediaSelectionGroupAVFObjC> create(AVPlayerItem*, AVMediaSelectionGroup*, const Vector<String>& characteristics);
     ~MediaSelectionGroupAVFObjC();
 
     void setSelectedOption(MediaSelectionOptionAVFObjC*);
     MediaSelectionOptionAVFObjC* selectedOption() const { return m_selectedOption; }
 
-    void updateOptions();
+    void updateOptions(const Vector<String>& characteristics);
 
     typedef HashMap<AVMediaSelectionOption*, RefPtr<MediaSelectionOptionAVFObjC>> OptionContainer;
     WTF::IteratorRange<OptionContainer::iterator::Values> options() { return m_options.values(); }
@@ -81,7 +82,7 @@ public:
     AVMediaSelectionGroup *avMediaSelectionGroup() const { return m_mediaSelectionGroup.get(); }
 
 private:
-    MediaSelectionGroupAVFObjC(AVPlayerItem*, AVMediaSelectionGroup*);
+    MediaSelectionGroupAVFObjC(AVPlayerItem*, AVMediaSelectionGroup*, const Vector<String>& characteristics);
 
     void selectionTimerFired();
 
index 3fc35ca..d136d87 100644 (file)
@@ -43,6 +43,11 @@ SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionGroup)
 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionOption)
 
+#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
+#include <MediaAccessibility/MediaAccessibility.h>
+#include "MediaAccessibilitySoftLink.h"
+#endif
+
 namespace WebCore {
 
 PassRefPtr<MediaSelectionOptionAVFObjC> MediaSelectionOptionAVFObjC::create(MediaSelectionGroupAVFObjC& group, AVMediaSelectionOption *option)
@@ -82,17 +87,17 @@ int MediaSelectionOptionAVFObjC::index() const
     return [[m_group->avMediaSelectionGroup() options] indexOfObject:m_mediaSelectionOption.get()];
 }
 
-PassRefPtr<MediaSelectionGroupAVFObjC> MediaSelectionGroupAVFObjC::create(AVPlayerItem *item, AVMediaSelectionGroup *group)
+PassRefPtr<MediaSelectionGroupAVFObjC> MediaSelectionGroupAVFObjC::create(AVPlayerItem *item, AVMediaSelectionGroup *group, const Vector<String>& characteristics)
 {
-    return adoptRef(new MediaSelectionGroupAVFObjC(item, group));
+    return adoptRef(new MediaSelectionGroupAVFObjC(item, group, characteristics));
 }
 
-MediaSelectionGroupAVFObjC::MediaSelectionGroupAVFObjC(AVPlayerItem *item, AVMediaSelectionGroup *group)
+MediaSelectionGroupAVFObjC::MediaSelectionGroupAVFObjC(AVPlayerItem *item, AVMediaSelectionGroup *group, const Vector<String>& characteristics)
     : m_playerItem(item)
     , m_mediaSelectionGroup(group)
     , m_selectionTimer(*this, &MediaSelectionGroupAVFObjC::selectionTimerFired)
 {
-    updateOptions();
+    updateOptions(characteristics);
 }
 
 MediaSelectionGroupAVFObjC::~MediaSelectionGroupAVFObjC()
@@ -101,7 +106,7 @@ MediaSelectionGroupAVFObjC::~MediaSelectionGroupAVFObjC()
         option->clearGroup();
 }
 
-void MediaSelectionGroupAVFObjC::updateOptions()
+void MediaSelectionGroupAVFObjC::updateOptions(const Vector<String>& characteristics)
 {
     RetainPtr<NSSet> newAVOptions = adoptNS([[NSSet alloc] initWithArray:[getAVMediaSelectionGroupClass() playableMediaSelectionOptionsFromArray:[m_mediaSelectionGroup options]]]);
     RetainPtr<NSMutableSet> oldAVOptions = adoptNS([[NSMutableSet alloc] initWithCapacity:m_options.size()]);
@@ -138,6 +143,21 @@ void MediaSelectionGroupAVFObjC::updateOptions()
         [nsLanguages addObject:(NSString*)language];
     NSArray* filteredOptions = [getAVMediaSelectionGroupClass() mediaSelectionOptionsFromArray:[m_mediaSelectionGroup options] filteredAndSortedAccordingToPreferredLanguages:nsLanguages.get()];
 
+    if (![filteredOptions count] && characteristics.isEmpty())
+        return;
+
+    // If no options match our language selection, search for matching characteristics across all the group's options
+    if (![filteredOptions count])
+        filteredOptions = [m_mediaSelectionGroup options];
+
+    RetainPtr<NSMutableArray> nsCharacteristics = adoptNS([[NSMutableArray alloc] initWithCapacity:characteristics.size()]);
+    for (auto& characteristic : characteristics)
+        [nsCharacteristics addObject:(NSString *)characteristic];
+
+    NSArray* optionsWithCharacteristics = [getAVMediaSelectionGroupClass() mediaSelectionOptionsFromArray:filteredOptions withMediaCharacteristics:nsCharacteristics.get()];
+    if (optionsWithCharacteristics && [optionsWithCharacteristics count])
+        filteredOptions = optionsWithCharacteristics;
+
     if (![filteredOptions count])
         return;
 
index e3590d2..ab6de21 100644 (file)
@@ -295,6 +295,8 @@ private:
 
     virtual URL resolvedURL() const override;
 
+    Vector<String> preferredAudioCharacteristics() const;
+
     WeakPtrFactory<MediaPlayerPrivateAVFoundationObjC> m_weakPtrFactory;
 
     RetainPtr<AVURLAsset> m_avAsset;
index 0dd2bad..480880e 100644 (file)
@@ -1981,9 +1981,9 @@ void determineChangedTracksFromNewTracksAndOldItems(NSArray* tracks, NSString* t
 
 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
 template <typename RefT, typename PassRefT>
-void determineChangedTracksFromNewTracksAndOldItems(MediaSelectionGroupAVFObjC* group, Vector<RefT>& oldItems, RefT (*itemFactory)(MediaSelectionOptionAVFObjC&), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
+void determineChangedTracksFromNewTracksAndOldItems(MediaSelectionGroupAVFObjC* group, Vector<RefT>& oldItems, const Vector<String>& characteristics, RefT (*itemFactory)(MediaSelectionOptionAVFObjC&), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
 {
-    group->updateOptions();
+    group->updateOptions(characteristics);
 
     // Only add selection options which do not have an associated persistant track.
     ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> newSelectionOptions;
@@ -2050,13 +2050,14 @@ void MediaPlayerPrivateAVFoundationObjC::updateAudioTracks()
     determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeAudio, m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
 
 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
+    Vector<String> characteristics = player()->preferredAudioCharacteristics();
     if (!m_audibleGroup) {
         if (AVMediaSelectionGroupType *group = safeMediaSelectionGroupForAudibleMedia())
-            m_audibleGroup = MediaSelectionGroupAVFObjC::create(m_avPlayerItem.get(), group);
+            m_audibleGroup = MediaSelectionGroupAVFObjC::create(m_avPlayerItem.get(), group, characteristics);
     }
 
     if (m_audibleGroup)
-        determineChangedTracksFromNewTracksAndOldItems(m_audibleGroup.get(), m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
+        determineChangedTracksFromNewTracksAndOldItems(m_audibleGroup.get(), m_audioTracks, characteristics, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
 #endif
 
     for (auto& track : m_audioTracks)
@@ -2078,11 +2079,11 @@ void MediaPlayerPrivateAVFoundationObjC::updateVideoTracks()
 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
     if (!m_visualGroup) {
         if (AVMediaSelectionGroupType *group = safeMediaSelectionGroupForVisualMedia())
-            m_visualGroup = MediaSelectionGroupAVFObjC::create(m_avPlayerItem.get(), group);
+            m_visualGroup = MediaSelectionGroupAVFObjC::create(m_avPlayerItem.get(), group, Vector<String>());
     }
 
     if (m_visualGroup)
-        determineChangedTracksFromNewTracksAndOldItems(m_visualGroup.get(), m_videoTracks, &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
+        determineChangedTracksFromNewTracksAndOldItems(m_visualGroup.get(), m_videoTracks, Vector<String>(), &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
 #endif
 
     for (auto& track : m_audioTracks)
index d54a0f5..a50a9da 100644 (file)
     } \
     }
 
+#define SOFT_LINK_CONSTANT_MAY_FAIL_FOR_HEADER(functionNamespace, framework, variableName, variableType) \
+    WTF_EXTERN_C_BEGIN \
+    extern const variableType variableName; \
+    WTF_EXTERN_C_END \
+    namespace functionNamespace { \
+    bool canLoad_##framework##_##variableName(); \
+    bool init_##framework##_##variableName(); \
+    variableType get_##framework##_##variableName(); \
+    }
+
+#define SOFT_LINK_CONSTANT_MAY_FAIL_FOR_SOURCE(functionNamespace, framework, variableName, variableType) \
+    WTF_EXTERN_C_BEGIN \
+    extern const variableType variableName; \
+    WTF_EXTERN_C_END \
+    namespace functionNamespace { \
+    static variableType constant##framework##variableName; \
+    bool init_##framework##_##variableName(); \
+    bool init_##framework##_##variableName() \
+    { \
+        void* constant = dlsym(framework##Library(), #variableName); \
+        if (!constant) \
+            return false; \
+        constant##framework##variableName = *static_cast<variableType*>(constant); \
+        return true; \
+    } \
+    bool canLoad_##framework##_##variableName(); \
+    bool canLoad_##framework##_##variableName() \
+    { \
+        static bool loaded = init_##framework##_##variableName(); \
+        return loaded; \
+    } \
+    variableType get_##framework##_##variableName(); \
+    variableType get_##framework##_##variableName() \
+    { \
+        return constant##framework##variableName; \
+    } \
+    }
+
 #define SOFT_LINK_FUNCTION_FOR_HEADER(functionNamespace, framework, functionName, resultType, parameterDeclarations, parameterNames) \
     WTF_EXTERN_C_BEGIN \
     resultType functionName parameterDeclarations; \
     } \
     }
 
+#define SOFT_LINK_FUNCTION_MAY_FAIL_FOR_HEADER(functionNamespace, framework, functionName, resultType, parameterDeclarations, parameterNames) \
+    WTF_EXTERN_C_BEGIN \
+    resultType functionName parameterDeclarations; \
+    WTF_EXTERN_C_END \
+    namespace functionNamespace { \
+    extern resultType (*softLink##framework##functionName) parameterDeclarations; \
+    bool canLoad_##framework##_##functionName(); \
+    bool init_##framework##_##functionName(); \
+    resultType softLink_##framework##_##functionName parameterDeclarations; \
+    }
+
+#define SOFT_LINK_FUNCTION_MAY_FAIL_FOR_SOURCE(functionNamespace, framework, functionName, resultType, parameterDeclarations, parameterNames) \
+    WTF_EXTERN_C_BEGIN \
+    resultType functionName parameterDeclarations; \
+    WTF_EXTERN_C_END \
+    namespace functionNamespace { \
+    resultType (*softLink##framework##functionName) parameterDeclarations = 0; \
+    bool init_##framework##_##functionName(); \
+    bool init_##framework##_##functionName() \
+    { \
+        ASSERT(!softLink##framework##functionName); \
+        softLink##framework##functionName = (resultType (*) parameterDeclarations) dlsym(framework##Library(), #functionName); \
+        return !!softLink##framework##functionName; \
+    } \
+    \
+    bool canLoad_##framework##_##functionName(); \
+    bool canLoad_##framework##_##functionName() \
+    { \
+        static bool loaded = init_##framework##_##functionName(); \
+        return loaded; \
+    } \
+    \
+    resultType softLink_##framework##_##functionName parameterDeclarations; \
+    resultType softLink_##framework##_##functionName parameterDeclarations \
+    { \
+        ASSERT(softLink##framework##functionName); \
+        return softLink##framework##functionName parameterNames; \
+    } \
+    }
+
 #define SOFT_LINK_POINTER_FOR_HEADER(functionNamespace, framework, variableName, variableType) \
     namespace functionNamespace { \
     extern variableType (*get_##framework##_##variableName)(); \
index feb4606..01fab79 100644 (file)
     } \
     }
 
+#define SOFT_LINK_CONSTANT_MAY_FAIL_FOR_HEADER(functionNamespace, framework, variableName, variableType) \
+    namespace functionNamespace { \
+    bool canLoad_##framework##_##variableName(); \
+    bool init_##framework##_##variableName(); \
+    variableType get_##framework##_##variableName(); \
+    }
+
+#define SOFT_LINK_CONSTANT_MAY_FAIL_FOR_SOURCE(functionNamespace, framework, variableName, variableType) \
+    namespace functionNamespace { \
+    static variableType constant##framework##variableName; \
+    bool init_##framework##_##variableName(); \
+    bool init_##framework##_##variableName() \
+    { \
+        variableType* ptr = reinterpret_cast<variableType*>(SOFT_LINK_GETPROCADDRESS(framework##Library(), #variableName)); \
+        if (!ptr) \
+            return false; \
+        constant##framework##variableName = *ptr; \
+        return true; \
+    } \
+    bool canLoad_##framework##_##variableName(); \
+    bool canLoad_##framework##_##variableName() \
+    { \
+        static bool loaded = init_##framework##_##variableName(); \
+        return loaded; \
+    } \
+    variableType get_##framework##_##variableName(); \
+    variableType get_##framework##_##variableName() \
+    { \
+        return constant##framework##variableName; \
+    } \
+    }
+
 #define SOFT_LINK_FUNCTION_FOR_HEADER(functionNamespace, framework, functionName, resultType, parameterDeclarations, parameterNames) \
     namespace functionNamespace { \
     extern resultType(__cdecl*softLink##framework##functionName) parameterDeclarations; \
     } \
     }
 
+#define SOFT_LINK_FUNCTION_MAY_FAIL_FOR_HEADER(functionNamespace, framework, functionName, resultType, parameterDeclarations, parameterNames) \
+    WTF_EXTERN_C_BEGIN \
+    resultType functionName parameterDeclarations; \
+    WTF_EXTERN_C_END \
+    namespace functionNamespace { \
+    extern resultType (*softLink##framework##functionName) parameterDeclarations; \
+    bool canLoad_##framework##_##functionName(); \
+    bool init_##framework##_##functionName(); \
+    resultType softLink_##framework##_##functionName parameterDeclarations; \
+    }
+
+#define SOFT_LINK_FUNCTION_MAY_FAIL_FOR_SOURCE(functionNamespace, framework, functionName, resultType, parameterDeclarations, parameterNames) \
+    WTF_EXTERN_C_BEGIN \
+    resultType functionName parameterDeclarations; \
+    WTF_EXTERN_C_END \
+    namespace functionNamespace { \
+    resultType (*softLink##framework##functionName) parameterDeclarations = 0; \
+    bool init_##framework##_##functionName(); \
+    bool init_##framework##_##functionName() \
+    { \
+        ASSERT(!softLink##framework##functionName); \
+        softLink##framework##functionName = reinterpret_cast<resultType (__cdecl*)parameterDeclarations>(SOFT_LINK_GETPROCADDRESS(framework##Library(), #functionName)); \
+        return !!softLink##framework##functionName; \
+    } \
+    \
+    bool canLoad_##framework##_##functionName(); \
+    bool canLoad_##framework##_##functionName() \
+    { \
+        static bool loaded = init_##framework##_##functionName(); \
+        return loaded; \
+    } \
+    \
+    resultType softLink_##framework##_##functionName parameterDeclarations; \
+    resultType softLink_##framework##_##functionName parameterDeclarations \
+    { \
+        ASSERT(softLink##framework##functionName); \
+        return softLink##framework##functionName parameterNames; \
+    } \
+    }
+
 #endif // SoftLinking_h
index 2826c2b..1034e8b 100644 (file)
@@ -1293,6 +1293,22 @@ void Internals::setUserPreferredLanguages(const Vector<String>& languages)
     WebCore::overrideUserPreferredLanguages(languages);
 }
 
+Vector<String> Internals::userPreferredAudioCharacteristics() const
+{
+    Document* document = contextDocument();
+    if (!document || !document->page())
+        return Vector<String>();
+    return document->page()->group().captionPreferences()->preferredAudioCharacteristics();
+}
+
+void Internals::setUserPreferredAudioCharacteristic(const String& characteristic)
+{
+    Document* document = contextDocument();
+    if (!document || !document->page())
+        return;
+    document->page()->group().captionPreferences()->setPreferredAudioCharacteristic(characteristic);
+}
+
 unsigned Internals::wheelEventHandlerCount(ExceptionCode& ec)
 {
     Document* document = contextDocument();
index 4d3c853..a9984a6 100644 (file)
@@ -190,6 +190,9 @@ public:
     Vector<String> userPreferredLanguages() const;
     void setUserPreferredLanguages(const Vector<String>&);
 
+    Vector<String> userPreferredAudioCharacteristics() const;
+    void setUserPreferredAudioCharacteristic(const String&);
+
     unsigned wheelEventHandlerCount(ExceptionCode&);
     unsigned touchEventHandlerCount(ExceptionCode&);
 
index b8a8b73..fae911e 100644 (file)
@@ -160,6 +160,9 @@ enum ResourceLoadPriority {
     sequence<DOMString> userPreferredLanguages();
     void setUserPreferredLanguages(sequence<DOMString> languages);
 
+    sequence<DOMString> userPreferredAudioCharacteristics();
+    void setUserPreferredAudioCharacteristic(DOMString characteristic);
+
     [RaisesException] unsigned long wheelEventHandlerCount();
     [RaisesException] unsigned long touchEventHandlerCount();