Allow ports to disable automatic text track selection
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Feb 2016 16:38:02 +0000 (16:38 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Feb 2016 16:38:02 +0000 (16:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153761
<rdar://problem/24416768>

Reviewed by Darin Adler.

Source/WebCore:

Test: media/track/track-manual-mode.html

* Modules/mediacontrols/MediaControlsHost.cpp:
(WebCore::MediaControlsHost::manualKeyword): New.
(WebCore::MediaControlsHost::captionDisplayMode): Support 'manual' mode.
* Modules/mediacontrols/MediaControlsHost.h:

* Modules/mediacontrols/mediaControlsApple.js:
(Controller.prototype.buildCaptionMenu): Check the 'off' item when in manual mode.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::addTextTrack): Update m_captionDisplayMode when called for the first
  time so it is always correct. Set the track's manual selection mode as appropriate.
(WebCore::HTMLMediaElement::captionPreferencesChanged): Set each track's manual selection
  mode as appropriate.

* html/track/TextTrack.cpp:
(WebCore::TextTrack::kind): Return 'subtitles' for forced tracks when in manual mode.
* html/track/TextTrack.h:

* html/track/TrackBase.h:
(WebCore::TrackBase::kind): Make virtual.

* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::beginBlockingNotifications): New.
(WebCore::CaptionUserPreferences::endBlockingNotifications): Ditto.
(WebCore::CaptionUserPreferences::notify): Don't notify when blocked.
* page/CaptionUserPreferences.h:

* page/CaptionUserPreferencesMediaAF.cpp:
(WebCore::CaptionUserPreferencesMediaAF::CaptionUserPreferencesMediaAF): Set manual mode
  when appropriate.
(WebCore::CaptionUserPreferencesMediaAF::captionDisplayMode): Check manual mode.
(WebCore::CaptionUserPreferencesMediaAF::setCaptionDisplayMode): Ditto.
(WebCore::CaptionUserPreferencesMediaAF::setPreferredLanguage): Ditto.
(WebCore::CaptionUserPreferencesMediaAF::textTrackSelectionScore): Return zero when in manual mode.
(WebCore::CaptionUserPreferencesMediaAF::sortedTrackListForMenu): Consider manual mode. Fix
  typos in logging.

* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::mediaDescriptionForKind): Return 'auxiliary' when in manual mode.

* testing/Internals.cpp:
(WebCore::Internals::setCaptionDisplayMode): Support manual mode.

LayoutTests:

* media/track/track-manual-mode-expected.txt: Added.
* media/track/track-manual-mode.html: Added.

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/media/track/track-manual-mode-expected.txt [new file with mode: 0644]
LayoutTests/media/track/track-manual-mode.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp
Source/WebCore/Modules/mediacontrols/MediaControlsHost.h
Source/WebCore/Modules/mediacontrols/mediaControlsApple.js
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/track/TextTrack.cpp
Source/WebCore/html/track/TextTrack.h
Source/WebCore/html/track/TrackBase.h
Source/WebCore/page/CaptionUserPreferences.cpp
Source/WebCore/page/CaptionUserPreferences.h
Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
Source/WebCore/testing/Internals.cpp

index cc3d2cf..f5478d3 100644 (file)
@@ -1,3 +1,14 @@
+2016-02-02  Eric Carlson  <eric.carlson@apple.com>
+
+        Allow ports to disable automatic text track selection
+        https://bugs.webkit.org/show_bug.cgi?id=153761
+        <rdar://problem/24416768>
+
+        Reviewed by Darin Adler.
+
+        * media/track/track-manual-mode-expected.txt: Added.
+        * media/track/track-manual-mode.html: Added.
+
 2016-02-02  Zalan Bujtas  <zalan@apple.com>
 
         [Win] gardening after r195740. (more to follow)
diff --git a/LayoutTests/media/track/track-manual-mode-expected.txt b/LayoutTests/media/track/track-manual-mode-expected.txt
new file mode 100644 (file)
index 0000000..89b1e8c
--- /dev/null
@@ -0,0 +1,53 @@
+Tests 'manual' caption mode.
+
+
+RUN(internals.setUserPreferredLanguages(['en']))
+RUN(internals.setPrimaryAudioTrackLanguageOverride('fr'))
+RUN(internals.setCaptionDisplayMode('manual'))
+EVENT(canplaythrough)
+
+** Forced tracks should be in .textTracks as well as in the menu,
+** but should be labeled as 'subtitles'
+
+EXPECTED (video.textTracks.length == '9') OK
+EXPECTED (trackMenuItems.length == '11') OK
+
+** No track should be enabled by default
+EXPECTED (video.textTracks[0].language == 'en') OK
+EXPECTED (video.textTracks[0].kind == 'subtitles') OK
+EXPECTED (video.textTracks[0].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[1].language == 'en') OK
+EXPECTED (video.textTracks[1].kind == 'subtitles') OK
+EXPECTED (video.textTracks[1].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[2].language == 'fr') OK
+EXPECTED (video.textTracks[2].kind == 'subtitles') OK
+EXPECTED (video.textTracks[2].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[3].language == 'fr') OK
+EXPECTED (video.textTracks[3].kind == 'subtitles') OK
+EXPECTED (video.textTracks[3].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[4].language == 'es') OK
+EXPECTED (video.textTracks[4].kind == 'subtitles') OK
+EXPECTED (video.textTracks[4].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[5].language == 'es') OK
+EXPECTED (video.textTracks[5].kind == 'subtitles') OK
+EXPECTED (video.textTracks[5].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[6].language == 'de') OK
+EXPECTED (video.textTracks[6].kind == 'subtitles') OK
+EXPECTED (video.textTracks[6].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[7].language == 'de') OK
+EXPECTED (video.textTracks[7].kind == 'subtitles') OK
+EXPECTED (video.textTracks[7].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[8].language == 'en') OK
+EXPECTED (video.textTracks[8].kind == 'captions') OK
+EXPECTED (video.textTracks[8].mode == 'disabled') OK
+
+END OF TEST
+
diff --git a/LayoutTests/media/track/track-manual-mode.html b/LayoutTests/media/track/track-manual-mode.html
new file mode 100644 (file)
index 0000000..a5312c0
--- /dev/null
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+        <script src=../media-controls.js></script>
+        <script src=../trackmenu-test.js></script>
+        <script src=../video-test.js></script>
+        <script>
+
+            function testTracks()
+            {
+                consoleWrite(`<br><i>** Forced tracks should be in .textTracks as well as in the menu,`);
+                consoleWrite(`<i>** but should be labeled as 'subtitles'</i><br>`);
+                testExpected("video.textTracks.length", 9);
+
+                // Click the CC button to show the menu because it isn't created until it becomes visible.
+                clickCCButton();
+
+                trackMenuItems = captionTrackMenuList();
+                if (!trackMenuItems) {
+                    failTest("Failed to find track menu!");
+                    return;
+                }
+                testExpected("trackMenuItems.length", 11);
+
+                consoleWrite("<br><i>** No track should be enabled by default<" + "/i>");
+                testExpected("video.textTracks[0].language", "en");
+                testExpected("video.textTracks[0].kind", "subtitles");
+                testExpected("video.textTracks[0].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[1].language", "en");
+                testExpected("video.textTracks[1].kind", "subtitles");
+                testExpected("video.textTracks[1].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[2].language", "fr");
+                testExpected("video.textTracks[2].kind", "subtitles");
+                testExpected("video.textTracks[2].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[3].language", "fr");
+                testExpected("video.textTracks[3].kind", "subtitles");
+                testExpected("video.textTracks[3].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[4].language", "es");
+                testExpected("video.textTracks[4].kind", "subtitles");
+                testExpected("video.textTracks[4].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[5].language", "es");
+                testExpected("video.textTracks[5].kind", "subtitles");
+                testExpected("video.textTracks[5].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[6].language", "de");
+                testExpected("video.textTracks[6].kind", "subtitles");
+                testExpected("video.textTracks[6].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[7].language", "de");
+                testExpected("video.textTracks[7].kind", "subtitles");
+                testExpected("video.textTracks[7].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[8].language", "en");
+                testExpected("video.textTracks[8].kind", "captions");
+                testExpected("video.textTracks[8].mode", "disabled");
+                consoleWrite("");
+
+                endTest();
+            }
+
+            function setup()
+            {
+                findMediaElement();
+                run("internals.setUserPreferredLanguages(['en'])");
+                run("internals.setPrimaryAudioTrackLanguageOverride('fr')");
+                run("internals.setCaptionDisplayMode('manual')");
+                video.src = '../content/CC+Subtitles.mov';
+
+                waitForEvent('canplaythrough', testTracks);
+            }
+
+        </script>
+    </head>
+    <body onload="setup()">
+        <p>Tests 'manual' caption mode.</p>
+        <video width="640" height="360" controls>
+        </video>
+    </body>
+</html>
index 36331db..47177a2 100644 (file)
@@ -1,3 +1,56 @@
+2016-02-02  Eric Carlson  <eric.carlson@apple.com>
+
+        Allow ports to disable automatic text track selection
+        https://bugs.webkit.org/show_bug.cgi?id=153761
+        <rdar://problem/24416768>
+
+        Reviewed by Darin Adler.
+
+        Test: media/track/track-manual-mode.html
+
+        * Modules/mediacontrols/MediaControlsHost.cpp:
+        (WebCore::MediaControlsHost::manualKeyword): New.
+        (WebCore::MediaControlsHost::captionDisplayMode): Support 'manual' mode.
+        * Modules/mediacontrols/MediaControlsHost.h:
+
+        * Modules/mediacontrols/mediaControlsApple.js:
+        (Controller.prototype.buildCaptionMenu): Check the 'off' item when in manual mode.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::addTextTrack): Update m_captionDisplayMode when called for the first
+          time so it is always correct. Set the track's manual selection mode as appropriate.
+        (WebCore::HTMLMediaElement::captionPreferencesChanged): Set each track's manual selection 
+          mode as appropriate.
+
+        * html/track/TextTrack.cpp:
+        (WebCore::TextTrack::kind): Return 'subtitles' for forced tracks when in manual mode.
+        * html/track/TextTrack.h:
+
+        * html/track/TrackBase.h:
+        (WebCore::TrackBase::kind): Make virtual.
+
+        * page/CaptionUserPreferences.cpp:
+        (WebCore::CaptionUserPreferences::beginBlockingNotifications): New.
+        (WebCore::CaptionUserPreferences::endBlockingNotifications): Ditto.
+        (WebCore::CaptionUserPreferences::notify): Don't notify when blocked.
+        * page/CaptionUserPreferences.h:
+
+        * page/CaptionUserPreferencesMediaAF.cpp:
+        (WebCore::CaptionUserPreferencesMediaAF::CaptionUserPreferencesMediaAF): Set manual mode 
+          when appropriate.
+        (WebCore::CaptionUserPreferencesMediaAF::captionDisplayMode): Check manual mode.
+        (WebCore::CaptionUserPreferencesMediaAF::setCaptionDisplayMode): Ditto.
+        (WebCore::CaptionUserPreferencesMediaAF::setPreferredLanguage): Ditto.
+        (WebCore::CaptionUserPreferencesMediaAF::textTrackSelectionScore): Return zero when in manual mode.
+        (WebCore::CaptionUserPreferencesMediaAF::sortedTrackListForMenu): Consider manual mode. Fix
+          typos in logging.
+
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::mediaDescriptionForKind): Return 'auxiliary' when in manual mode.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::setCaptionDisplayMode): Support manual mode.
+
 2016-02-02  Adrien Plazas  <aplazas@igalia.com>
 
         REGRESSION(r195899): ASSERTION FAILED: is<Target>(source) in EventPath::retargetTouch() since r195899
index 8a8c6ff..c6fda71 100644 (file)
@@ -61,6 +61,13 @@ const AtomicString& MediaControlsHost::alwaysOnKeyword()
     return alwaysOn;
 }
 
+const AtomicString& MediaControlsHost::manualKeyword()
+{
+    static NeverDestroyed<const AtomicString> alwaysOn("manual", AtomicString::ConstructFromLiteral);
+    return alwaysOn;
+}
+
+
 Ref<MediaControlsHost> MediaControlsHost::create(HTMLMediaElement* mediaElement)
 {
     return adoptRef(*new MediaControlsHost(mediaElement));
@@ -151,6 +158,8 @@ AtomicString MediaControlsHost::captionDisplayMode()
         return forcedOnlyKeyword();
     case CaptionUserPreferences::AlwaysOn:
         return alwaysOnKeyword();
+    case CaptionUserPreferences::Manual:
+        return manualKeyword();
     default:
         ASSERT_NOT_REACHED();
         return emptyAtom;
index dc052a4..1957713 100644 (file)
@@ -52,6 +52,7 @@ public:
     static const AtomicString& automaticKeyword();
     static const AtomicString& forcedOnlyKeyword();
     static const AtomicString& alwaysOnKeyword();
+    static const AtomicString& manualKeyword();
 
     Vector<RefPtr<TextTrack>> sortedTrackListForMenu(TextTrackList*);
     Vector<RefPtr<AudioTrack>> sortedTrackListForMenu(AudioTrackList*);
index 95122a9..1885c38 100644 (file)
@@ -1836,7 +1836,7 @@ Controller.prototype = {
 
             }
 
-            if (offMenu && displayMode === 'forced-only' && !trackMenuItemSelected) {
+            if (offMenu && (displayMode === 'forced-only' || displayMode === 'manual') && !trackMenuItemSelected) {
                 offMenu.classList.add(this.ClassNames.selected);
                 offMenu.setAttribute('tabindex', '0');
                 offMenu.setAttribute('aria-checked', 'true');
index dd14bec..4a829e0 100644 (file)
@@ -3576,9 +3576,13 @@ void HTMLMediaElement::addTextTrack(PassRefPtr<TextTrack> track)
 
     if (!m_requireCaptionPreferencesChangedCallbacks) {
         m_requireCaptionPreferencesChangedCallbacks = true;
-        document().registerForCaptionPreferencesChangedCallbacks(this);
+        Document& document = this->document();
+        document.registerForCaptionPreferencesChangedCallbacks(this);
+        if (Page* page = document.page())
+            m_captionDisplayMode = page->group().captionPreferences()->captionDisplayMode();
     }
 
+    track->setManualSelectionMode(m_captionDisplayMode == CaptionUserPreferences::Manual);
     textTracks()->append(track);
 
     closeCaptionTracksChanged();
@@ -5723,6 +5727,11 @@ void HTMLMediaElement::captionPreferencesChanged()
     if (m_captionDisplayMode == displayMode)
         return;
 
+    if (m_captionDisplayMode == CaptionUserPreferences::Manual || displayMode == CaptionUserPreferences::Manual) {
+        for (unsigned i = 0; i < m_textTracks->length(); ++i)
+            m_textTracks->item(i)->setManualSelectionMode(displayMode == CaptionUserPreferences::Manual);
+    }
+
     m_captionDisplayMode = displayMode;
     setWebkitClosedCaptionsVisible(m_captionDisplayMode == CaptionUserPreferences::AlwaysOn);
 }
index 729f520..ce3e373 100644 (file)
@@ -173,6 +173,15 @@ bool TextTrack::isValidKindKeyword(const AtomicString& value)
     return false;
 }
 
+AtomicString TextTrack::kind() const
+{
+    AtomicString kind = TrackBase::kind();
+    if (!m_manualSelectionMode || kind != forcedKeyword())
+        return kind;
+
+    return subtitlesKeyword();
+}
+
 void TextTrack::setKind(const AtomicString& newKind)
 {
     String oldKind = kind();
index ba1ae9a..05391e8 100644 (file)
@@ -82,7 +82,8 @@ public:
     static const AtomicString& hiddenKeyword();
     static const AtomicString& showingKeyword();
 
-    virtual void setKind(const AtomicString&) override;
+    void setKind(const AtomicString&) override;
+    AtomicString kind() const override;
 
     virtual AtomicString inBandMetadataTrackDispatchType() const { return emptyString(); }
 
@@ -142,6 +143,8 @@ public:
 
     virtual MediaTime startTimeVariance() const { return MediaTime::zeroTime(); }
 
+    void setManualSelectionMode(bool mode) { m_manualSelectionMode = mode; }
+
     using RefCounted<TrackBase>::ref;
     using RefCounted<TrackBase>::deref;
 
@@ -171,6 +174,7 @@ private:
     int m_trackIndex;
     int m_renderedTrackIndex;
     bool m_hasBeenConfigured;
+    bool m_manualSelectionMode { false };
 };
 
 inline TextTrack* toTextTrack(TrackBase* track)
index 245988b..1506d72 100644 (file)
@@ -51,7 +51,7 @@ public:
     virtual AtomicString id() const { return m_id; }
     virtual void setId(const AtomicString& id) { m_id = id; }
 
-    AtomicString kind() const { return m_kind; }
+    virtual AtomicString kind() const { return m_kind; }
     virtual void setKind(const AtomicString&);
 
     AtomicString label() const { return m_label; }
index 163ebb6..fd754e7 100644 (file)
@@ -62,8 +62,22 @@ void CaptionUserPreferences::timerFired()
     captionPreferencesChanged();
 }
 
+void CaptionUserPreferences::beginBlockingNotifications()
+{
+    ++m_blockNotificationsCounter;
+}
+
+void CaptionUserPreferences::endBlockingNotifications()
+{
+    ASSERT(m_blockNotificationsCounter);
+    --m_blockNotificationsCounter;
+}
+
 void CaptionUserPreferences::notify()
 {
+    if (m_blockNotificationsCounter)
+        return;
+
     m_havePreferences = true;
     if (!m_timer.isActive())
         m_timer.startOneShot(0);
index 43efa2e..8b08bbb 100644 (file)
@@ -50,7 +50,8 @@ public:
     enum CaptionDisplayMode {
         Automatic,
         ForcedOnly,
-        AlwaysOn
+        AlwaysOn,
+        Manual,
     };
     virtual CaptionDisplayMode captionDisplayMode() const;
     virtual void setCaptionDisplayMode(CaptionDisplayMode);
@@ -98,18 +99,21 @@ public:
 
 protected:
     void updateCaptionStyleSheetOveride();
+    void beginBlockingNotifications();
+    void endBlockingNotifications();
 
 private:
     void timerFired();
     void notify();
 
     PageGroup& m_pageGroup;
-    CaptionDisplayMode m_displayMode;
+    mutable CaptionDisplayMode m_displayMode;
     Timer m_timer;
     String m_userPreferredLanguage;
     String m_userPreferredAudioCharacteristic;
     String m_captionsStyleSheetOverride;
     String m_primaryAudioTrackLanguageOverride;
+    unsigned m_blockNotificationsCounter { 0 };
     bool m_testingMode;
     bool m_havePreferences;
 };
index 72cab1a..42ae170 100644 (file)
@@ -75,8 +75,18 @@ SOFT_LINK_AVF_POINTER(CoreText, kCTFontNameAttribute, CFStringRef)
 #define kCTFontNameAttribute getkCTFontNameAttribute()
 
 #define CTFontDescriptorCopyAttribute softLink_CTFontDescriptorCopyAttribute
-#endif
-#endif
+
+typedef Boolean (*MTEnableCaption2015BehaviorPtrType) ();
+static MTEnableCaption2015BehaviorPtrType MTEnableCaption2015BehaviorPtr() { return nullptr; }
+
+#else
+
+SOFT_LINK_FRAMEWORK(MediaToolbox)
+SOFT_LINK_OPTIONAL(MediaToolbox, MTEnableCaption2015Behavior, Boolean, (), ())
+
+#endif // PLATFORM(WIN)
+
+#endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
 
 namespace WebCore {
 
@@ -100,6 +110,21 @@ CaptionUserPreferencesMediaAF::CaptionUserPreferencesMediaAF(PageGroup& group)
     , m_listeningForPreferenceChanges(false)
 #endif
 {
+    static bool initialized;
+    if (!initialized) {
+        initialized = true;
+
+        MTEnableCaption2015BehaviorPtrType function = MTEnableCaption2015BehaviorPtr();
+        if (!function || !function())
+            return;
+
+        beginBlockingNotifications();
+        CaptionUserPreferences::setCaptionDisplayMode(Manual);
+        setUserPrefersCaptions(false);
+        setUserPrefersSubtitles(false);
+        setUserPrefersTextDescriptions(false);
+        endBlockingNotifications();
+    }
 }
 
 CaptionUserPreferencesMediaAF::~CaptionUserPreferencesMediaAF()
@@ -116,8 +141,9 @@ CaptionUserPreferencesMediaAF::~CaptionUserPreferencesMediaAF()
 
 CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferencesMediaAF::captionDisplayMode() const
 {
-    if (testingMode() || !MediaAccessibilityLibrary())
-        return CaptionUserPreferences::captionDisplayMode();
+    CaptionDisplayMode internalMode = CaptionUserPreferences::captionDisplayMode();
+    if (internalMode == Manual || testingMode() || !MediaAccessibilityLibrary())
+        return internalMode;
 
     MACaptionAppearanceDisplayType displayType = MACaptionAppearanceGetDisplayType(kMACaptionAppearanceDomainUser);
     switch (displayType) {
@@ -142,6 +168,9 @@ void CaptionUserPreferencesMediaAF::setCaptionDisplayMode(CaptionUserPreferences
         return;
     }
 
+    if (captionDisplayMode() == Manual)
+        return;
+
     MACaptionAppearanceDisplayType displayType = kMACaptionAppearanceDisplayTypeForcedOnly;
     switch (mode) {
     case Automatic:
@@ -432,6 +461,9 @@ float CaptionUserPreferencesMediaAF::captionFontSizeScaleAndImportance(bool& imp
 
 void CaptionUserPreferencesMediaAF::setPreferredLanguage(const String& language)
 {
+    if (CaptionUserPreferences::captionDisplayMode() == Manual)
+        return;
+
     if (testingMode() || !MediaAccessibilityLibrary()) {
         CaptionUserPreferences::setPreferredLanguage(language);
         return;
@@ -661,6 +693,9 @@ String CaptionUserPreferencesMediaAF::displayNameForTrack(TextTrack* track) cons
 int CaptionUserPreferencesMediaAF::textTrackSelectionScore(TextTrack* track, HTMLMediaElement* mediaElement) const
 {
     CaptionDisplayMode displayMode = captionDisplayMode();
+    if (displayMode == Manual)
+        return 0;
+
     bool legacyOverride = mediaElement->webkitClosedCaptionsVisible();
     if (displayMode == AlwaysOn && (!userPrefersSubtitles() && !userPrefersCaptions() && !legacyOverride))
         return 0;
@@ -804,6 +839,7 @@ Vector<RefPtr<TextTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(
 
     Vector<RefPtr<TextTrack>> tracksForMenu;
     HashSet<String> languagesIncluded;
+    CaptionDisplayMode displayMode = captionDisplayMode();
     bool prefersAccessibilityTracks = userPrefersCaptions();
     bool filterTrackList = shouldFilterTrackMenu();
 
@@ -811,17 +847,23 @@ Vector<RefPtr<TextTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(
         TextTrack* track = trackList->item(i);
         String language = displayNameForLanguageLocale(track->language());
 
+        if (displayMode == Manual) {
+            LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because selection mode is 'manual'", track->kind().string().utf8().data(), language.utf8().data());
+            tracksForMenu.append(track);
+            continue;
+        }
+
         const AtomicString& kind = track->kind();
         if (kind != TextTrack::captionsKeyword() && kind != TextTrack::descriptionsKeyword() && kind != TextTrack::subtitlesKeyword())
             continue;
 
         if (track->containsOnlyForcedSubtitles()) {
-            LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - skipping '%s' track with language '%s' because it contains only forced subtitles", track->kind().string().utf8().data(), language.utf8().data());
+            LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - skipping '%s' track with language '%s' because it contains only forced subtitles", track->kind().string().utf8().data(), language.utf8().data());
             continue;
         }
         
         if (track->isEasyToRead()) {
-            LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - adding '%s' track with language '%s' because it is 'easy to read'", track->kind().string().utf8().data(), language.utf8().data());
+            LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because it is 'easy to read'", track->kind().string().utf8().data(), language.utf8().data());
             if (!language.isEmpty())
                 languagesIncluded.add(language);
             tracksForMenu.append(track);
@@ -829,7 +871,7 @@ Vector<RefPtr<TextTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(
         }
 
         if (track->mode() == TextTrack::showingKeyword()) {
-            LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - adding '%s' track with language '%s' because it is already visible", track->kind().string().utf8().data(), language.utf8().data());
+            LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because it is already visible", track->kind().string().utf8().data(), language.utf8().data());
             if (!language.isEmpty())
                 languagesIncluded.add(language);
             tracksForMenu.append(track);
@@ -861,7 +903,7 @@ Vector<RefPtr<TextTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(
             languagesIncluded.add(language);
         tracksForMenu.append(track);
 
-        LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - adding '%s' track with language '%s', is%s main program content", track->kind().string().utf8().data(), language.utf8().data(), track->isMainProgramContent() ? "" : " NOT");
+        LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s', is%s main program content", track->kind().string().utf8().data(), language.utf8().data(), track->isMainProgramContent() ? "" : " NOT");
     }
 
     // Now that we have filtered for the user's accessibility/translation preference, add  all tracks with a unique language without regard to track type.
@@ -869,6 +911,9 @@ Vector<RefPtr<TextTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(
         TextTrack* track = trackList->item(i);
         String language = displayNameForLanguageLocale(track->language());
 
+        if (tracksForMenu.contains(track))
+            continue;
+
         const AtomicString& kind = track->kind();
         if (kind != TextTrack::captionsKeyword() && kind != TextTrack::descriptionsKeyword() && kind != TextTrack::subtitlesKeyword())
             continue;
index 87fac7e..efa3d70 100644 (file)
@@ -255,6 +255,7 @@ SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIdentifierKey, NSString
 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackSourceKey, NSString*)
 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, NSString*)
 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, NSString*)
+SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicIsAuxiliaryContent, NSString*)
 
 #define AVURLAssetHTTPCookiesKey getAVURLAssetHTTPCookiesKey()
 #define AVURLAssetOutOfBandAlternateTracksKey getAVURLAssetOutOfBandAlternateTracksKey()
@@ -266,6 +267,7 @@ SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForA
 #define AVOutOfBandAlternateTrackSourceKey getAVOutOfBandAlternateTrackSourceKey()
 #define AVMediaCharacteristicDescribesMusicAndSoundForAccessibility getAVMediaCharacteristicDescribesMusicAndSoundForAccessibility()
 #define AVMediaCharacteristicTranscribesSpokenDialogForAccessibility getAVMediaCharacteristicTranscribesSpokenDialogForAccessibility()
+#define AVMediaCharacteristicIsAuxiliaryContent getAVMediaCharacteristicIsAuxiliaryContent()
 #endif
 
 #if ENABLE(DATACUE_VALUE)
@@ -305,6 +307,9 @@ SOFT_LINK_POINTER(CoreVideo, kCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey, N
 #define kCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey getkCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey()
 #endif
 
+SOFT_LINK_FRAMEWORK(MediaToolbox)
+SOFT_LINK_OPTIONAL(MediaToolbox, MTEnableCaption2015Behavior, Boolean, (), ())
+
 using namespace WebCore;
 
 enum MediaPlayerAVFoundationObservationContext {
@@ -732,6 +737,10 @@ bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
 #if ENABLE(AVF_CAPTIONS)
 static const NSArray* mediaDescriptionForKind(PlatformTextTrack::TrackKind kind)
 {
+    static bool manualSelectionMode = MTEnableCaption2015BehaviorPtr() && MTEnableCaption2015BehaviorPtr()();
+    if (manualSelectionMode)
+        return @[ AVMediaCharacteristicIsAuxiliaryContent ];
+
     // FIXME: Match these to correct types:
     if (kind == PlatformTextTrack::Caption)
         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
index 6f968a0..dd0f965 100644 (file)
@@ -2784,6 +2784,8 @@ void Internals::setCaptionDisplayMode(const String& mode, ExceptionCode& ec)
         captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::ForcedOnly);
     else if (equalLettersIgnoringASCIICase(mode, "alwayson"))
         captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::AlwaysOn);
+    else if (equalLettersIgnoringASCIICase(mode, "manual"))
+        captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::Manual);
     else
         ec = SYNTAX_ERR;
 #else