[Mac] add "automatic" text track menu item
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Apr 2013 22:23:47 +0000 (22:23 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Apr 2013 22:23:47 +0000 (22:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=113822

Reviewed by Jer Noble.

Source/WebCore:

No new tests, existing tests updated.

* English.lproj/Localizable.strings: Add new track menu item.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement): Initialize m_captionDisplayMode.
(WebCore::HTMLMediaElement::finishParsingChildren): LoadTextTrackResource -> ConfigureTextTracks.
(WebCore::HTMLMediaElement::scheduleDelayedAction): Ditto.
(WebCore::HTMLMediaElement::loadTimerFired): Ditto.
(WebCore::HTMLMediaElement::loadInternal): Ditto. Flag the caption menu as invalid so it will
    be rebuilt again.
(WebCore::HTMLMediaElement::mediaPlayerDidAddTrack): LoadTextTrackResource -> ConfigureTextTracks.
(WebCore::HTMLMediaElement::didAddTrack): Ditto.
(WebCore::HTMLMediaElement::configureTextTrackGroup): Deal with new track display modes.
(WebCore::HTMLMediaElement::HTMLMediaElement::hasClosedCaptions): Minor cleanup.
(WebCore::HTMLMediaElement::configureTextTrackGroup): Add some logging.
(WebCore::HTMLMediaElement::configureTextTracks): Call updateActiveTextTrackCues so cue display
    is updated.
(WebCore::HTMLMediaElement::captionPreferencesChanged): Deal with new track display modes.
(WebCore::HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured): Add some logging.
* html/HTMLMediaElement.h: Ditto.

* html/shadow/MediaControlElementTypes.cpp: Remove trackIndexAttributeName and trackListIndexForElement.
* html/shadow/MediaControlElementTypes.h: Ditto.

* html/shadow/MediaControlElements.cpp:
(WebCore::MediaControlClosedCaptionsTrackListElement::defaultEventHandler): Don't use track index,
    everything we need is in the track<->element map now.
(WebCore::MediaControlClosedCaptionsTrackListElement::updateDisplay): Ditto. Always rebuild the
    track list because clear the map each time the menu goes away because it refs every track.
(WebCore::MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu): Don't set the track
    index attribute on the menu items, just use the map.
* html/shadow/MediaControlElements.h:

* html/shadow/MediaControlsApple.cpp:
(WebCore::MediaControlsApple::changedClosedCaptionsVisibility): resetTrackListMenu is gone.
(WebCore::MediaControlsApple::reset): Ditto.
(WebCore::MediaControlsApple::closedCaptionTracksChanged): Ditto.

* html/track/TextTrack.cpp:
(WebCore::TextTrack::captionMenuOffItem): New, static TextTrack used for the menu "Off" item.
(WebCore::TextTrack::captionMenuAutomaticItem): New, static TextTrack used for the menu "Automatic" item.
* html/track/TextTrack.h:

* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::shouldShowCaptions): Removed, we use captionDisplayMode instead.
(WebCore::CaptionUserPreferences::captionDisplayMode): New.
(WebCore::CaptionUserPreferences::setCaptionDisplayMode): New.
(WebCore::CaptionUserPreferences::setPreferredLanguage): Make the parameter a const reference.
(WebCore::CaptionUserPreferences::textTrackSelectionScore): Return 0 if the user doesn't want
    captions or subtitles.
(WebCore::CaptionUserPreferences::textTrackLanguageSelectionScore): Take language as a parameter
    instead of assuming that it is always the entire language list.
* page/CaptionUserPreferences.h:

* page/CaptionUserPreferencesMac.h:
* page/CaptionUserPreferencesMac.mm:
(WebCore::CaptionUserPreferencesMac::shouldShowCaptions): Removed, we use captionDisplayMode instead.
(WebCore::CaptionUserPreferencesMac::captionDisplayMode): New.
(WebCore::CaptionUserPreferencesMac::setCaptionDisplayMode): New.
(WebCore::CaptionUserPreferencesMac::setPreferredLanguage): Make the parameter a const reference.
(WebCore::CaptionUserPreferencesMac::textTrackSelectionScore): Deal with "Automatic" mode.
    captions or subtitles.
(WebCore::CaptionUserPreferences::textTrackLanguageSelectionScore): Take language as a parameter
    instead of assuming that it is always the entire language list.
(WebCore::CaptionUserPreferencesMac::sortedTrackListForMenu): Insert "Off" and "Automatic" items.

* platform/LocalizedStrings.cpp:
(WebCore::textTrackAutomaticMenuItemText): New.
* platform/LocalizedStrings.h:

* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::languageOfPrimaryAudioTrack): New.
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:

* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack): Return the language
    of the main audio track.

LayoutTests:

* media/media-captions.html: Opportunistic fix - don't look at state until we get the 'canplaythrough'
    event because the media engine state may not have been processed until that time.
* media/track/track-cue-container-rendering-position.html: Don't assume that a cue has been
    rendered immediately when the 'canplaythrough' event comes through.
* media/track/track-cue-rendering-mode-changed.html: Ditto.
* media/track/track-in-band-expected.txt: Don't check if cues have loaded, it doesn't matter
    for this test.
* media/track/track-in-band.html: Ditto.
* media/track/track-user-preferences-expected.txt: Changed name of utility function from
    menuIndexForLanguage to indexOfMenuItemBeginningWith.
* media/track/track-user-preferences.html: Move menuIndexForLanguage to trackmenu-test.js and
    change name to indexOfMenuItemBeginningWith. Clarify the message logged when a track loads
    but it should not.
* media/trackmenu-test.js: Move some functions here from track-user-preferences.html
(trackMenuList):
(indexOfMenuItemBeginningWith):
(selectCaptionMenuItem):
* media/video-controls-captions-trackmenu-sorted.html: Update for new menu item.
* media/video-controls-captions-trackmenu.html: Ditto.
* platform/mac/media/video-controls-captions-trackmenu-expected.txt: Ditto.
* platform/mac/media/video-controls-captions-trackmenu-sorted-expected.txt: Ditto.

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

35 files changed:
LayoutTests/ChangeLog
LayoutTests/media/media-captions.html
LayoutTests/media/track/track-cue-container-rendering-position.html
LayoutTests/media/track/track-cue-rendering-mode-changed.html
LayoutTests/media/track/track-in-band-expected.txt
LayoutTests/media/track/track-in-band.html
LayoutTests/media/track/track-user-preferences-expected.txt
LayoutTests/media/track/track-user-preferences.html
LayoutTests/media/trackmenu-test.js
LayoutTests/media/video-controls-captions-trackmenu-sorted.html
LayoutTests/media/video-controls-captions-trackmenu.html
LayoutTests/platform/mac/media/video-controls-captions-trackmenu-expected.txt
LayoutTests/platform/mac/media/video-controls-captions-trackmenu-sorted-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/English.lproj/Localizable.strings
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/html/shadow/MediaControlElementTypes.cpp
Source/WebCore/html/shadow/MediaControlElementTypes.h
Source/WebCore/html/shadow/MediaControlElements.cpp
Source/WebCore/html/shadow/MediaControlElements.h
Source/WebCore/html/shadow/MediaControlsApple.cpp
Source/WebCore/html/track/TextTrack.cpp
Source/WebCore/html/track/TextTrack.h
Source/WebCore/page/CaptionUserPreferences.cpp
Source/WebCore/page/CaptionUserPreferences.h
Source/WebCore/page/CaptionUserPreferencesMac.h
Source/WebCore/page/CaptionUserPreferencesMac.mm
Source/WebCore/platform/LocalizedStrings.cpp
Source/WebCore/platform/LocalizedStrings.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm

index 7cc3403..1757b44 100644 (file)
@@ -1,3 +1,32 @@
+2013-04-04  Eric Carlson  <eric.carlson@apple.com>
+
+        [Mac] add "automatic" text track menu item
+        https://bugs.webkit.org/show_bug.cgi?id=113822
+
+        Reviewed by Jer Noble.
+
+        * media/media-captions.html: Opportunistic fix - don't look at state until we get the 'canplaythrough'
+            event because the media engine state may not have been processed until that time.
+        * media/track/track-cue-container-rendering-position.html: Don't assume that a cue has been 
+            rendered immediately when the 'canplaythrough' event comes through.
+        * media/track/track-cue-rendering-mode-changed.html: Ditto.
+        * media/track/track-in-band-expected.txt: Don't check if cues have loaded, it doesn't matter
+            for this test.
+        * media/track/track-in-band.html: Ditto.
+        * media/track/track-user-preferences-expected.txt: Changed name of utility function from 
+            menuIndexForLanguage to indexOfMenuItemBeginningWith.
+        * media/track/track-user-preferences.html: Move menuIndexForLanguage to trackmenu-test.js and
+            change name to indexOfMenuItemBeginningWith. Clarify the message logged when a track loads
+            but it should not.
+        * media/trackmenu-test.js: Move some functions here from track-user-preferences.html
+        (trackMenuList):
+        (indexOfMenuItemBeginningWith):
+        (selectCaptionMenuItem):
+        * media/video-controls-captions-trackmenu-sorted.html: Update for new menu item.
+        * media/video-controls-captions-trackmenu.html: Ditto.
+        * platform/mac/media/video-controls-captions-trackmenu-expected.txt: Ditto.
+        * platform/mac/media/video-controls-captions-trackmenu-sorted-expected.txt: Ditto.
+
 2013-04-04  Glenn Adams  <glenn@skynav.com>
 
         [EFL] Unreviewed gardening. Rebaseline after r147588. Remove overridden pixel results.
index b2f4565..86aeec0 100644 (file)
@@ -74,7 +74,7 @@
                 waitForEvent("play");
                 waitForEvent("playing");
 
-                waitForEvent('loadedmetadata', canplaythrough);
+                waitForEvent('canplaythrough', canplaythrough);
 
                 consoleWrite("<b>Test before movie is open:</b>");
                 testExpected("mediaElement.webkitHasClosedCaptions", false);
index 2167967..df0b7a3 100644 (file)
@@ -30,7 +30,7 @@
 
             findMediaElement();
             video.src = findMediaFile('video', '../content/test');
-            waitForEvent('canplaythrough', testPosition);
+            waitForEvent('canplaythrough', function () {setTimeout(testPosition, 100);});
         }
 
         </script>
index fe033a4..dc3f104 100644 (file)
         var testTrackArabic;
         var testTrackEnglish;
         var testCueDisplayBox;
+        var currentTest = 0;
+
+        function scheduleNextTest() 
+        {
+            var test;
+            switch (++currentTest) {
+            case 1:
+                test = addTracks;
+                break;
+            case 2:
+                test = testCueActiveState;
+                break;
+            case 3:
+                test = testCueVisibility1;
+                break;
+            case 4:
+                test = testCueVisibility2;
+                break;
+            case 5:
+                endTest();
+                return;
+            }
+
+            window.setTimeout(test, 100);
+        }
 
         function addTracks()
         {
@@ -31,6 +56,8 @@
 
             consoleWrite("Set the mode of the 'English' track to hidden");
             testTrackEnglish.mode = "hidden";
+
+            scheduleNextTest();
         }
 
         function testCueActiveState()
 
             testExpected("testTrackArabic.activeCues.length", 1);
             testExpected("testTrackArabic.activeCues[0].text", "Arabic");
+
+            scheduleNextTest();
         }
 
-        function testCueVisibility()
+        function testCueVisibility1()
         {
             consoleWrite("");
             consoleWrite("** Only one cue should be visible **");
             consoleWrite("Set the mode of the 'English' track to showing");
             testTrackEnglish.mode = "showing";
 
+            scheduleNextTest();
+        }
+
+        function testCueVisibility2()
+        {
             consoleWrite("");
             consoleWrite("** Both cues shold be visible. **");
             testCueDisplayBox = textTrackDisplayElement(video, 'display', 0);
 
             testCueDisplayBox = textTrackDisplayElement(video, 'display', 1);
             testExpected("testCueDisplayBox.innerText", "English");
-        }
 
-        function runTests() {
-            addTracks();
-            testCueActiveState();
-            testCueVisibility();
-            endTest();
+            scheduleNextTest();
         }
 
         function loaded()
             findMediaElement();
             video.src = findMediaFile('video', '../content/test');
 
-            waitForEvent('canplaythrough', runTests);
+            waitForEvent('canplaythrough', scheduleNextTest);
         }
 
         </script>
index 4eb06ae..66d7e71 100644 (file)
@@ -12,12 +12,8 @@ EVENT(canplaythrough)
 EXPECTED (video.textTracks.length == '2') OK
 RUN(inbandTrack1 = video.textTracks[0])
 RUN(inbandTrack2 = video.textTracks[1])
-EXPECTED (inbandTrack1.mode == 'disabled') OK
-EXPECTED (inbandTrack1.cues == 'null') OK
 EXPECTED (inbandTrack1.language == 'en') OK
 EXPECTED (inbandTrack1.kind == 'subtitles') OK
-EXPECTED (inbandTrack2.mode == 'disabled') OK
-EXPECTED (inbandTrack2.cues == 'null') OK
 EXPECTED (inbandTrack2.language == 'fr') OK
 EXPECTED (inbandTrack2.kind == 'subtitles') OK
 
index 0aa91e8..9c90e7b 100644 (file)
                 testExpected("video.textTracks.length", 2);
                 run("inbandTrack1 = video.textTracks[0]");
                 run("inbandTrack2 = video.textTracks[1]");
-                testExpected("inbandTrack1.mode", "disabled");
-                testExpected("inbandTrack1.cues", null);
                 testExpected("inbandTrack1.language", "en");
                 testExpected("inbandTrack1.kind", "subtitles");
-                testExpected("inbandTrack2.mode", "disabled");
-                testExpected("inbandTrack2.cues", null);
                 testExpected("inbandTrack2.language", "fr");
                 testExpected("inbandTrack2.kind", "subtitles");
 
index 9fe1feb..335f451 100644 (file)
@@ -18,9 +18,9 @@ EXPECTED (event.target.readyState == '2') OK
 
 Test 3: select 'fr' track from menu
 - show captions menu.
-EXPECTED (trackMenuItems[menuIndexForLanguage('French')].className == '') OK
-EXPECTED (trackMenuItems[menuIndexForLanguage('English')].className == '') OK
-EXPECTED (trackMenuItems[menuIndexForLanguage('Norwegian')].className == 'selected') OK
+EXPECTED (trackMenuItems[indexOfMenuItemBeginningWith('French')].className == '') OK
+EXPECTED (trackMenuItems[indexOfMenuItemBeginningWith('English')].className == '') OK
+EXPECTED (trackMenuItems[indexOfMenuItemBeginningWith('Norwegian')].className == 'selected') OK
 - click on 'French' menu item.
 EVENT(load)
 EXPECTED (event.target.srclang == 'fr') OK
index cc359a3..7a5c649 100644 (file)
                 timer = setTimeout(nextStep, 100);
             }
 
-            function trackMenuList()
-            {
-                trackListElement = getTrackListElement();
-                if (!trackListElement)
-                    return;
-
-                // Track list should have a <ul> with <li> children (One of them is "Off").
-                var trackList = trackListElement.querySelector("ul");
-                if (!trackList) {
-                    failTest("Could not find a child ul element in track list menu");
-                    return;
-                }
-                var trackListItems = trackList.querySelectorAll("li");
-                if (!trackListItems) {
-                    failTest("Could not find child li elements in track list menu");
-                    return;
-                }
-
-                return trackListItems;
-            }
-
-            function menuIndexForLanguage(language)
-            {
-                var trackMenuItems = trackMenuList();
-                for (i = 0; i < trackMenuItems.length; ++i) {
-                    if (trackMenuItems[i].textContent.indexOf(language) >= 0)
-                        break;
-                }
-                return (i < trackMenuItems.length) ? i : -1;
-            }
-
-            function selectCaptionMenuItem(language)
-            {
-                var trackMenuItems = trackMenuList();
-                var index = menuIndexForLanguage(language);
-                if (index < 0) {
-                    failTest("Menu item " + language + " not found in track list menu.");
-                    return;
-                }
-
-                consoleWrite("- click on '" + language + "' menu item.");
-                var selectedTrackItem = trackMenuItems[index];
-                var boundingRect = selectedTrackItem.getBoundingClientRect();
-                var x = boundingRect.left + boundingRect.width / 2;
-                var y = boundingRect.top + boundingRect.height / 2;
-                eventSender.mouseMoveTo(x, y);
-                eventSender.mouseDown();
-                eventSender.mouseUp();
-            }
-
             function trackLoaded()
             {
                 consoleWrite("EVENT(load)");
+                if (expectedLanguage == "") {
+                    failTest("'" + event.target.srclang + "' track loaded unexpectedly.");
+                    return;
+                }
                 testExpected("event.target.srclang", expectedLanguage);
                 testExpected("event.target.readyState", HTMLTrackElement.LOADED);
 
 
                 case 4:
                     trackMenuItems = trackMenuList();
-                    testExpected("trackMenuItems[menuIndexForLanguage('French')].className", "");
-                    testExpected("trackMenuItems[menuIndexForLanguage('English')].className", "");
-                    testExpected("trackMenuItems[menuIndexForLanguage('Norwegian')].className", "selected");
+                    testExpected("trackMenuItems[indexOfMenuItemBeginningWith('French')].className", "");
+                    testExpected("trackMenuItems[indexOfMenuItemBeginningWith('English')].className", "");
+                    testExpected("trackMenuItems[indexOfMenuItemBeginningWith('Norwegian')].className", "selected");
 
                     selectCaptionMenuItem("French");
                     expectedLanguage = 'fr';
 
                 case 7:
                     selectCaptionMenuItem("Off");
+                    expectedLanguage = '';
                     createTrackElements([ 'ru', 'jp', 'en']);
                     timer = setTimeout(nextStep, 100);
                     break;
index e327d6a..2fcd4b5 100644 (file)
@@ -35,3 +35,54 @@ function getTrackListElement()
   }
   return trackListElement;
 }
+
+function trackMenuList()
+{
+    trackListElement = getTrackListElement();
+    if (!trackListElement)
+        return;
+
+    // Track list should have a <ul> with <li> children.
+    var trackList = trackListElement.querySelector("ul");
+    if (!trackList) {
+        failTest("Could not find a child ul element in track list menu");
+        return;
+    }
+    var trackListItems = trackList.querySelectorAll("li");
+    if (!trackListItems) {
+        failTest("Could not find child li elements in track list menu");
+        return;
+    }
+
+    return trackListItems;
+}
+
+function indexOfMenuItemBeginningWith(title)
+{
+    var trackMenuItems = trackMenuList();
+    for (i = 0; i < trackMenuItems.length; ++i) {
+        if (trackMenuItems[i].textContent.indexOf(title) == 0)
+            break;
+    }
+    return (i < trackMenuItems.length) ? i : -1;
+}
+
+function selectCaptionMenuItem(title)
+{
+    var trackMenuItems = trackMenuList();
+    var index = indexOfMenuItemBeginningWith(title);
+    if (index < 0) {
+        failTest("Menu item " + title + " not found in track list menu.");
+        return;
+    }
+
+    consoleWrite("- click on '" + title + "' menu item.");
+    var selectedTrackItem = trackMenuItems[index];
+    var boundingRect = selectedTrackItem.getBoundingClientRect();
+    var x = boundingRect.left + boundingRect.width / 2;
+    var y = boundingRect.top + boundingRect.height / 2;
+    eventSender.mouseMoveTo(x, y);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+}
+
index 12f8e9e..922e026 100644 (file)
                 case 1:
                     trackMenuItems = textTrackMenuItems();
                     testExpected("trackMenuItems[0].textContent", "Off");
-                    testExpected("trackMenuItems[1].textContent", "u (English) SDH");
-                    testExpected("trackMenuItems[2].textContent", "y (English-Australia) SDH");
-                    testExpected("trackMenuItems[3].textContent", "v (English)");
-                    testExpected("trackMenuItems[4].textContent", "x (English-Australia)");
-                    testExpected("trackMenuItems[5].textContent", "w (Japanese) SDH");
-                    testExpected("trackMenuItems[6].textContent", "z (Japanese)");
+                    testExpected("trackMenuItems[1].textContent", "Automatic (English)");
+                    testExpected("trackMenuItems[2].textContent", "u (English) SDH");
+                    testExpected("trackMenuItems[3].textContent", "y (English-Australia) SDH");
+                    testExpected("trackMenuItems[4].textContent", "v (English)");
+                    testExpected("trackMenuItems[5].textContent", "x (English-Australia)");
+                    testExpected("trackMenuItems[6].textContent", "w (Japanese) SDH");
+                    testExpected("trackMenuItems[7].textContent", "z (Japanese)");
                     testExpected("video.textTracks.length", 6);
-                    testExpected("trackMenuItems.length", 7);
+                    testExpected("trackMenuItems.length", 8);
 
                     consoleWrite("<br><i>** Change preferred language to Japanese, change preference to subtitles<" + "/i>");
                     run("internals.setUserPreferredLanguages(['ja'])");
                 case 2:
                     trackMenuItems = textTrackMenuItems();
                     testExpected("trackMenuItems[0].textContent", "Off");
-                    testExpected("trackMenuItems[1].textContent", "z (Japanese)");
-                    testExpected("trackMenuItems[2].textContent", "v (English)");
-                    testExpected("trackMenuItems[3].textContent", "x (English-Australia)");
+                    testExpected("trackMenuItems[1].textContent", "Automatic (Japanese)");
+                    testExpected("trackMenuItems[2].textContent", "z (Japanese)");
+                    testExpected("trackMenuItems[3].textContent", "v (English)");
+                    testExpected("trackMenuItems[4].textContent", "x (English-Australia)");
                     testExpected("video.textTracks.length", 6);
-                    testExpected("trackMenuItems.length", 4);
+                    testExpected("trackMenuItems.length", 5);
 
                     consoleWrite("");
                     endTest();
index 90e76f1..6341e7d 100644 (file)
@@ -41,7 +41,7 @@
             function testMenu()
             {
                 trackListItems = trackMenuList();
-                var expectedItemCount = video.textTracks.length + 1;
+                var expectedItemCount = video.textTracks.length + 2;
                 consoleWrite("There should be " + expectedItemCount + " items in the menu.");
                 testExpected("trackListItems.length", expectedItemCount);
             }
@@ -63,8 +63,8 @@
             function turnCaptionsOn()
             {
                 consoleWrite("*** Turning captions on");
-                // Click on the second item, which is the second track (Off is the first item in the menu)
-                selectCaptionMenuItem(2, testCaptionsVisible);
+                // Click on the third item, which is the second track (Off is the first item, Automatic is second)
+                selectCaptionMenuItem(3, testCaptionsVisible);
             }
 
             function testCaptionsVisible()
index 5527d3e..e8f6a19 100644 (file)
@@ -9,8 +9,8 @@ RUN(internals.setUserPreferredLanguages(['en']))
 RUN(video.addTextTrack("captions", "Commentary", "ru"))
 
 *** Turning captions on
-There should be 5 items in the menu.
-EXPECTED (trackListItems.length == '5') OK
+There should be 6 items in the menu.
+EXPECTED (trackListItems.length == '6') OK
 EXPECTED (video.textTracks.length == '4') OK
 Track 0 should be showing
 EXPECTED (video.textTracks[0].mode == 'showing') OK
@@ -25,8 +25,8 @@ EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Lorem') OK
 *** Remove a track.
 RUN(video.removeChild(document.querySelectorAll("track")[0]))
 *** Turning captions off
-There should be 4 items in the menu.
-EXPECTED (trackListItems.length == '4') OK
+There should be 5 items in the menu.
+EXPECTED (trackListItems.length == '5') OK
 EXPECTED (video.textTracks.length == '3') OK
 Track 0 should be disabled
 EXPECTED (video.textTracks[0].mode == 'disabled') OK
index 99934a5..83adba9 100644 (file)
@@ -8,25 +8,27 @@ RUN(internals.setUserPreferredLanguages(['en']))
 ** Test with preferred language set to English, track type set to captions
 RUN(internals.settings.setShouldDisplayTrackKind('Captions', true))
 EXPECTED (trackMenuItems[0].textContent == 'Off') OK
-EXPECTED (trackMenuItems[1].textContent == 'u (English) SDH') OK
-EXPECTED (trackMenuItems[2].textContent == 'y (English-Australia) SDH') OK
-EXPECTED (trackMenuItems[3].textContent == 'v (English)') OK
-EXPECTED (trackMenuItems[4].textContent == 'x (English-Australia)') OK
-EXPECTED (trackMenuItems[5].textContent == 'w (Japanese) SDH') OK
-EXPECTED (trackMenuItems[6].textContent == 'z (Japanese)') OK
+EXPECTED (trackMenuItems[1].textContent == 'Automatic (English)') OK
+EXPECTED (trackMenuItems[2].textContent == 'u (English) SDH') OK
+EXPECTED (trackMenuItems[3].textContent == 'y (English-Australia) SDH') OK
+EXPECTED (trackMenuItems[4].textContent == 'v (English)') OK
+EXPECTED (trackMenuItems[5].textContent == 'x (English-Australia)') OK
+EXPECTED (trackMenuItems[6].textContent == 'w (Japanese) SDH') OK
+EXPECTED (trackMenuItems[7].textContent == 'z (Japanese)') OK
 EXPECTED (video.textTracks.length == '6') OK
-EXPECTED (trackMenuItems.length == '7') OK
+EXPECTED (trackMenuItems.length == '8') OK
 
 ** Change preferred language to Japanese, change preference to subtitles
 RUN(internals.setUserPreferredLanguages(['ja']))
 RUN(internals.settings.setShouldDisplayTrackKind('Captions', false))
 RUN(internals.settings.setShouldDisplayTrackKind('Subtitles', true))
 EXPECTED (trackMenuItems[0].textContent == 'Off') OK
-EXPECTED (trackMenuItems[1].textContent == 'z (Japanese)') OK
-EXPECTED (trackMenuItems[2].textContent == 'v (English)') OK
-EXPECTED (trackMenuItems[3].textContent == 'x (English-Australia)') OK
+EXPECTED (trackMenuItems[1].textContent == 'Automatic (Japanese)') OK
+EXPECTED (trackMenuItems[2].textContent == 'z (Japanese)') OK
+EXPECTED (trackMenuItems[3].textContent == 'v (English)') OK
+EXPECTED (trackMenuItems[4].textContent == 'x (English-Australia)') OK
 EXPECTED (video.textTracks.length == '6') OK
-EXPECTED (trackMenuItems.length == '4') OK
+EXPECTED (trackMenuItems.length == '5') OK
 
 END OF TEST
 
index c3107ab..32152cd 100644 (file)
@@ -1,3 +1,91 @@
+2013-04-04  Eric Carlson  <eric.carlson@apple.com>
+
+        [Mac] add "automatic" text track menu item
+        https://bugs.webkit.org/show_bug.cgi?id=113822
+
+        Reviewed by Jer Noble.
+
+        No new tests, existing tests updated.
+
+        * English.lproj/Localizable.strings: Add new track menu item.
+
+        * html/HTMLMediaElement.cpp: 
+        (WebCore::HTMLMediaElement::HTMLMediaElement): Initialize m_captionDisplayMode.
+        (WebCore::HTMLMediaElement::finishParsingChildren): LoadTextTrackResource -> ConfigureTextTracks.
+        (WebCore::HTMLMediaElement::scheduleDelayedAction): Ditto.
+        (WebCore::HTMLMediaElement::loadTimerFired): Ditto.
+        (WebCore::HTMLMediaElement::loadInternal): Ditto. Flag the caption menu as invalid so it will
+            be rebuilt again.
+        (WebCore::HTMLMediaElement::mediaPlayerDidAddTrack): LoadTextTrackResource -> ConfigureTextTracks.
+        (WebCore::HTMLMediaElement::didAddTrack): Ditto.
+        (WebCore::HTMLMediaElement::configureTextTrackGroup): Deal with new track display modes.
+        (WebCore::HTMLMediaElement::HTMLMediaElement::hasClosedCaptions): Minor cleanup.
+        (WebCore::HTMLMediaElement::configureTextTrackGroup): Add some logging.
+        (WebCore::HTMLMediaElement::configureTextTracks): Call updateActiveTextTrackCues so cue display
+            is updated.
+        (WebCore::HTMLMediaElement::captionPreferencesChanged): Deal with new track display modes.
+        (WebCore::HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured): Add some logging.
+        * html/HTMLMediaElement.h: Ditto.
+
+        * html/shadow/MediaControlElementTypes.cpp: Remove trackIndexAttributeName and trackListIndexForElement.
+        * html/shadow/MediaControlElementTypes.h: Ditto.
+
+        * html/shadow/MediaControlElements.cpp:
+        (WebCore::MediaControlClosedCaptionsTrackListElement::defaultEventHandler): Don't use track index,
+            everything we need is in the track<->element map now.
+        (WebCore::MediaControlClosedCaptionsTrackListElement::updateDisplay): Ditto. Always rebuild the 
+            track list because clear the map each time the menu goes away because it refs every track.
+        (WebCore::MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu): Don't set the track
+            index attribute on the menu items, just use the map.
+        * html/shadow/MediaControlElements.h:
+
+        * html/shadow/MediaControlsApple.cpp:
+        (WebCore::MediaControlsApple::changedClosedCaptionsVisibility): resetTrackListMenu is gone.
+        (WebCore::MediaControlsApple::reset): Ditto.
+        (WebCore::MediaControlsApple::closedCaptionTracksChanged): Ditto.
+
+        * html/track/TextTrack.cpp:
+        (WebCore::TextTrack::captionMenuOffItem): New, static TextTrack used for the menu "Off" item.
+        (WebCore::TextTrack::captionMenuAutomaticItem): New, static TextTrack used for the menu "Automatic" item.
+        * html/track/TextTrack.h:
+
+        * page/CaptionUserPreferences.cpp:
+        (WebCore::CaptionUserPreferences::shouldShowCaptions): Removed, we use captionDisplayMode instead.
+        (WebCore::CaptionUserPreferences::captionDisplayMode): New.
+        (WebCore::CaptionUserPreferences::setCaptionDisplayMode): New.
+        (WebCore::CaptionUserPreferences::setPreferredLanguage): Make the parameter a const reference.
+        (WebCore::CaptionUserPreferences::textTrackSelectionScore): Return 0 if the user doesn't want
+            captions or subtitles.
+        (WebCore::CaptionUserPreferences::textTrackLanguageSelectionScore): Take language as a parameter
+            instead of assuming that it is always the entire language list.
+        * page/CaptionUserPreferences.h:
+
+        * page/CaptionUserPreferencesMac.h:
+        * page/CaptionUserPreferencesMac.mm:
+        (WebCore::CaptionUserPreferencesMac::shouldShowCaptions): Removed, we use captionDisplayMode instead.
+        (WebCore::CaptionUserPreferencesMac::captionDisplayMode): New.
+        (WebCore::CaptionUserPreferencesMac::setCaptionDisplayMode): New.
+        (WebCore::CaptionUserPreferencesMac::setPreferredLanguage): Make the parameter a const reference.
+        (WebCore::CaptionUserPreferencesMac::textTrackSelectionScore): Deal with "Automatic" mode.
+            captions or subtitles.
+        (WebCore::CaptionUserPreferences::textTrackLanguageSelectionScore): Take language as a parameter
+            instead of assuming that it is always the entire language list.
+        (WebCore::CaptionUserPreferencesMac::sortedTrackListForMenu): Insert "Off" and "Automatic" items.
+
+        * platform/LocalizedStrings.cpp:
+        (WebCore::textTrackAutomaticMenuItemText): New.
+        * platform/LocalizedStrings.h:
+
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::languageOfPrimaryAudioTrack): New.
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerPrivate.h:
+
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack): Return the language
+            of the main audio track.
+
 2013-04-04  Alexey Proskuryakov  <ap@apple.com>
 
         REGRESSION (r146580): Assertion using SecurityOrigin::create, URL string is not valid
index 0020aa7..ff79452 100644 (file)
Binary files a/Source/WebCore/English.lproj/Localizable.strings and b/Source/WebCore/English.lproj/Localizable.strings differ
index 9f9006f..3a876cd 100644 (file)
@@ -295,6 +295,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
     , m_haveVisibleTextTrack(false)
     , m_processingPreferenceChange(false)
     , m_lastTextTrackUpdateTime(-1)
+    , m_captionDisplayMode(CaptionUserPreferences::Automatic)
     , m_textTracks(0)
     , m_ignoreTrackDisplayUpdate(0)
 #endif
@@ -496,7 +497,7 @@ void HTMLMediaElement::finishParsingChildren()
     
     for (Node* node = firstChild(); node; node = node->nextSibling()) {
         if (node->hasTagName(trackTag)) {
-            scheduleDelayedAction(LoadTextTrackResource);
+            scheduleDelayedAction(ConfigureTextTracks);
             break;
         }
     }
@@ -610,8 +611,8 @@ void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
     }
 
 #if ENABLE(VIDEO_TRACK)
-    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (actionType & LoadTextTrackResource))
-        m_pendingActionFlags |= LoadTextTrackResource;
+    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (actionType & ConfigureTextTracks))
+        m_pendingActionFlags |= ConfigureTextTracks;
 #endif
 
 #if USE(PLATFORM_TEXT_TRACK_MENU)
@@ -619,8 +620,7 @@ void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
         m_pendingActionFlags |= TextTrackChangesNotification;
 #endif
 
-    if (!m_loadTimer.isActive())
-        m_loadTimer.startOneShot(0);
+    m_loadTimer.startOneShot(0);
 }
 
 void HTMLMediaElement::scheduleNextSourceChild()
@@ -646,7 +646,7 @@ void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
     RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
 
 #if ENABLE(VIDEO_TRACK)
-    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & LoadTextTrackResource))
+    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & ConfigureTextTracks))
         configureTextTracks();
 #endif
 
@@ -830,6 +830,9 @@ void HTMLMediaElement::loadInternal()
     removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
 
 #if ENABLE(VIDEO_TRACK)
+    if (hasMediaControls())
+        mediaControls()->changedClosedCaptionsVisibility();
+
     // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
     // disabled state when the element's resource selection algorithm last started".
     if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
@@ -2821,7 +2824,7 @@ void HTMLMediaElement::mediaPlayerDidAddTrack(PassRefPtr<InbandTextTrackPrivate>
     // 7. Set the new text track's mode to the mode consistent with the user's preferences and the requirements of
     // the relevant specification for the data.
     //  - This will happen in configureTextTracks()
-    scheduleDelayedAction(LoadTextTrackResource);
+    scheduleDelayedAction(ConfigureTextTracks);
     
     // 8. Add the new text track to the media element's list of text tracks.
     // 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent
@@ -3023,7 +3026,7 @@ void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
     // Do not schedule the track loading until parsing finishes so we don't start before all tracks
     // in the markup have been added.
     if (!m_parsingInProgress)
-        scheduleDelayedAction(LoadTextTrackResource);
+        scheduleDelayedAction(ConfigureTextTracks);
 
     if (hasMediaControls())
         mediaControls()->closedCaptionTracksChanged();
@@ -3063,20 +3066,12 @@ void HTMLMediaElement::didRemoveTrack(HTMLTrackElement* trackElement)
         m_textTracksWhenResourceSelectionBegan.remove(index);
 }
 
-bool HTMLMediaElement::userPrefersCaptions() const
-{
-    Page* page = document()->page();
-    if (!page)
-        return false;
-
-    CaptionUserPreferences* captionPreferences = page->group().captionPreferences();
-    return captionPreferences->userHasCaptionPreferences() && captionPreferences->shouldShowCaptions();
-}
-
 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
 {
     ASSERT(group.tracks.size());
 
+    LOG(Media, "HTMLMediaElement::configureTextTrackGroup");
+
     Page* page = document()->page();
     CaptionUserPreferences* captionPreferences = page? page->group().captionPreferences() : 0;
 
@@ -3147,23 +3142,39 @@ void HTMLMediaElement::setSelectedTextTrack(TextTrack* trackToSelect)
     TextTrackList* trackList = textTracks();
     if (!trackList || !trackList->length())
         return;
-    if (trackToSelect && !trackList->contains(trackToSelect))
-        return;
 
-    for (int i = 0, length = trackList->length(); i < length; ++i) {
-        TextTrack* track = trackList->item(i);
-        if (!trackToSelect || track != trackToSelect)
-            track->setMode(TextTrack::disabledKeyword());
-        else
-            track->setMode(TextTrack::showingKeyword());
+    if (trackToSelect != TextTrack::captionMenuOffItem() && trackToSelect != TextTrack::captionMenuAutomaticItem()) {
+        if (!trackList->contains(trackToSelect))
+            return;
+        
+        for (int i = 0, length = trackList->length(); i < length; ++i) {
+            TextTrack* track = trackList->item(i);
+            if (!trackToSelect || track != trackToSelect)
+                track->setMode(TextTrack::disabledKeyword());
+            else
+                track->setMode(TextTrack::showingKeyword());
+        }
     }
 
     CaptionUserPreferences* captionPreferences = document()->page() ? document()->page()->group().captionPreferences() : 0;
-    if (captionPreferences) {
-        captionPreferences->setShouldShowCaptions(trackToSelect);
-        if (trackToSelect && trackToSelect->language().length())
+    if (!captionPreferences)
+        return;
+
+    CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences->captionDisplayMode();
+    if (trackToSelect == TextTrack::captionMenuOffItem())
+        displayMode = CaptionUserPreferences::ForcedOnly;
+    else if (trackToSelect == TextTrack::captionMenuAutomaticItem())
+        displayMode = CaptionUserPreferences::Automatic;
+    else {
+        displayMode = CaptionUserPreferences::AlwaysOn;
+        if (trackToSelect->language().length())
             captionPreferences->setPreferredLanguage(trackToSelect->language());
+        
+        // Set m_captionDisplayMode here so we don't reconfigure again when the preference changed notification comes through.
+        m_captionDisplayMode = displayMode;
     }
+
+    captionPreferences->setCaptionDisplayMode(displayMode);
 }
 
 void HTMLMediaElement::configureTextTracks()
@@ -4248,7 +4259,9 @@ bool HTMLMediaElement::hasClosedCaptions() const
         return true;
 
 #if ENABLE(VIDEO_TRACK)
-    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && m_textTracks)
+    if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled() || !m_textTracks)
+        return false;
+
     for (unsigned i = 0; i < m_textTracks->length(); ++i) {
         if (m_textTracks->item(i)->readinessState() == TextTrack::FailedToLoad)
             continue;
@@ -4258,6 +4271,7 @@ bool HTMLMediaElement::hasClosedCaptions() const
             return true;
     }
 #endif
+
     return false;
 }
 
@@ -4458,6 +4472,8 @@ void HTMLMediaElement::configureTextTrackDisplay()
     if (m_processingPreferenceChange)
         return;
 
+    LOG(Media, "HTMLMediaElement::configureTextTrackDisplay");
+
     bool haveVisibleTextTrack = false;
     for (unsigned i = 0; i < m_textTracks->length(); ++i) {
         if (m_textTracks->item(i)->mode() == TextTrack::showingKeyword()) {
@@ -4466,8 +4482,11 @@ void HTMLMediaElement::configureTextTrackDisplay()
         }
     }
 
-    if (m_haveVisibleTextTrack == haveVisibleTextTrack)
+    if (m_haveVisibleTextTrack == haveVisibleTextTrack) {
+        updateActiveTextTrackCues(currentTime());
         return;
+    }
+
     m_haveVisibleTextTrack = haveVisibleTextTrack;
     m_closedCaptionsVisible = m_haveVisibleTextTrack;
 
@@ -4478,8 +4497,10 @@ void HTMLMediaElement::configureTextTrackDisplay()
 
     mediaControls()->changedClosedCaptionsVisibility();
     
-    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
+    if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
         updateTextTrackDisplay();
+        updateActiveTextTrackCues(currentTime());
+    }
 }
 
 void HTMLMediaElement::captionPreferencesChanged()
@@ -4490,7 +4511,15 @@ void HTMLMediaElement::captionPreferencesChanged()
     if (hasMediaControls())
         mediaControls()->textTrackPreferencesChanged();
 
-    setClosedCaptionsVisible(userPrefersCaptions());
+    if (!document()->page())
+        return;
+
+    CaptionUserPreferences::CaptionDisplayMode displayMode = document()->page()->group().captionPreferences()->captionDisplayMode();
+    if (m_captionDisplayMode == displayMode)
+        return;
+
+    m_captionDisplayMode = displayMode;
+    setClosedCaptionsVisible(m_captionDisplayMode == CaptionUserPreferences::AlwaysOn);
 }
 
 void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured()
@@ -4498,6 +4527,8 @@ void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured()
     if (!m_textTracks)
         return;
 
+    LOG(Media, "HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured");
+
     // Mark all tracks as not "configured" so that configureTextTracks()
     // will reconsider which tracks to display in light of new user preferences
     // (e.g. default tracks should not be displayed if the user has turned off
index 11de6c6..ca016c0 100644 (file)
@@ -39,6 +39,7 @@
 #endif
 
 #if ENABLE(VIDEO_TRACK)
+#include "CaptionUserPreferences.h"
 #include "PODIntervalTree.h"
 #include "TextTrack.h"
 #include "TextTrackCue.h"
@@ -112,8 +113,9 @@ public:
 
     enum DelayedActionType {
         LoadMediaResource = 1 << 0,
-        LoadTextTrackResource = 1 << 1,
-        TextTrackChangesNotification = 1 << 2
+        ConfigureTextTracks = 1 << 1,
+        TextTrackChangesNotification = 1 << 2,
+        ConfigureTextTrackDisplay = 1 << 3,
     };
     void scheduleDelayedAction(DelayedActionType);
     
@@ -268,10 +270,7 @@ public:
     void configureTextTrackGroup(const TrackGroup&);
 
     void setSelectedTextTrack(TextTrack*);
-    static int textTracksOffIndex() { return -1; }
-    static int textTracksIndexNotFound() { return -2; }
 
-    bool userPrefersCaptions() const;
     bool textTracksAreReady() const;
     void configureTextTrackDisplay();
     void updateTextTrackDisplay();
@@ -696,7 +695,9 @@ private:
     bool m_tracksAreReady : 1;
     bool m_haveVisibleTextTrack : 1;
     bool m_processingPreferenceChange : 1;
+
     float m_lastTextTrackUpdateTime;
+    CaptionUserPreferences::CaptionDisplayMode m_captionDisplayMode;
 
     RefPtr<TextTrackList> m_textTracks;
     Vector<RefPtr<TextTrack> > m_textTracksWhenResourceSelectionBegan;
index 79efdcc..ed105f1 100644 (file)
@@ -75,26 +75,6 @@ MediaControlElementType mediaControlElementType(Node* node)
     return static_cast<MediaControlDivElement*>(element)->displayType();
 }
 
-#if ENABLE(VIDEO_TRACK)
-const AtomicString& trackIndexAttributeName()
-{
-    DEFINE_STATIC_LOCAL(AtomicString, name, ("x-webkit-track-index", AtomicString::ConstructFromLiteral));
-    return name;
-}
-
-int trackListIndexForElement(Element* element)
-{
-    const AtomicString trackIndexAttributeValue = element->getAttribute(trackIndexAttributeName());
-    if (trackIndexAttributeValue.isNull() || trackIndexAttributeValue.isEmpty())
-        return HTMLMediaElement::textTracksIndexNotFound();
-    bool ok;
-    int trackIndex = trackIndexAttributeValue.toInt(&ok);
-    if (!ok)
-        return HTMLMediaElement::textTracksIndexNotFound();
-    return trackIndex;
-}
-#endif
-
 MediaControlElement::MediaControlElement(MediaControlElementType displayType, HTMLElement* element)
     : m_mediaController(0)
     , m_displayType(displayType)
index c2f6791..d4b9fbe 100644 (file)
@@ -79,11 +79,6 @@ inline HTMLMediaElement* toParentMediaElement(RenderObject* renderer) { return t
 
 MediaControlElementType mediaControlElementType(Node*);
 
-#if ENABLE(VIDEO_TRACK)
-const AtomicString& trackIndexAttributeName();
-int trackListIndexForElement(Element*);
-#endif
-
 // ----------------------------
 
 class MediaControlElement {
index 2110d04..b288364 100644 (file)
@@ -70,10 +70,6 @@ using namespace HTMLNames;
 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
 
-#if ENABLE(VIDEO_TRACK)
-static const char* textTracksOffAttrValue = "-1"; // This must match HTMLMediaElement::textTracksOffIndex()
-#endif
-
 MediaControlPanelElement::MediaControlPanelElement(Document* document)
     : MediaControlDivElement(document, MediaControlsPanel)
     , m_canBeDragged(false)
@@ -758,7 +754,6 @@ const AtomicString& MediaControlClosedCaptionsContainerElement::shadowPseudoId()
 MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document* document, MediaControls* controls)
     : MediaControlDivElement(document, MediaClosedCaptionsTrackList)
     , m_controls(controls)
-    , m_trackListHasChanged(true)
 {
 }
 
@@ -788,19 +783,14 @@ void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* even
             textTrack = iter->value;
         m_menuToTrackMap.clear();
         m_controls->toggleClosedCaptionTrackList();
-
-        int trackIndex = trackListIndexForElement(toElement(target));
-        if (trackIndex == HTMLMediaElement::textTracksIndexNotFound())
+        if (!textTrack)
             return;
 
         HTMLMediaElement* mediaElement = toParentMediaElement(this);
         if (!mediaElement)
             return;
 
-        if (textTrack)
-            mediaElement->setSelectedTextTrack(textTrack.get());
-        else if (trackIndex == HTMLMediaElement::textTracksOffIndex())
-            mediaElement->setSelectedTextTrack(0);
+        mediaElement->setSelectedTextTrack(textTrack.get());
 
         updateDisplay();
     }
@@ -823,34 +813,24 @@ void MediaControlClosedCaptionsTrackListElement::updateDisplay()
     if (!mediaController()->hasClosedCaptions())
         return;
 
+    if (!document()->page())
+        return;
+    CaptionUserPreferences::CaptionDisplayMode displayMode = document()->page()->group().captionPreferences()->captionDisplayMode();
+    bool trackIsSelected = displayMode != CaptionUserPreferences::Automatic && displayMode != CaptionUserPreferences::ForcedOnly;
+
     HTMLMediaElement* mediaElement = toParentMediaElement(this);
     if (!mediaElement)
         return;
 
     TextTrackList* trackList = mediaElement->textTracks();
-
     if (!trackList || !trackList->length())
         return;
 
-    if (m_trackListHasChanged)
-        rebuildTrackListMenu();
+    rebuildTrackListMenu();
 
-    bool captionsVisible = mediaElement->closedCaptionsVisible();
     for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) {
         RefPtr<Element> trackItem = m_menuItems[i];
 
-        int trackIndex = trackListIndexForElement(trackItem.get());
-        if (trackIndex == HTMLMediaElement::textTracksIndexNotFound())
-            continue;
-
-        if (trackIndex == HTMLMediaElement::textTracksOffIndex()) {
-            if (captionsVisible)
-                trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
-            else
-                trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
-            continue;
-        }
-
         RefPtr<TextTrack> textTrack;
         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(trackItem.get());
         if (iter == m_menuToTrackMap.end())
@@ -858,7 +838,24 @@ void MediaControlClosedCaptionsTrackListElement::updateDisplay()
         textTrack = iter->value;
         if (!textTrack)
             continue;
-        if (textTrack->mode() == TextTrack::showingKeyword())
+
+        if (textTrack == TextTrack::captionMenuOffItem()) {
+            if (displayMode == CaptionUserPreferences::ForcedOnly)
+                trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
+            else
+                trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
+            continue;
+        }
+
+        if (textTrack == TextTrack::captionMenuAutomaticItem()) {
+            if (displayMode == CaptionUserPreferences::Automatic)
+                trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
+            else
+                trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
+            continue;
+        }
+
+        if (trackIsSelected && textTrack->mode() == TextTrack::showingKeyword())
             trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
         else
             trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
@@ -872,8 +869,6 @@ void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
     // Remove any existing content.
     removeChildren();
     m_menuItems.clear();
-
-    m_trackListHasChanged = false;
     m_menuToTrackMap.clear();
 
     if (!mediaController()->hasClosedCaptions())
@@ -888,41 +883,26 @@ void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
         return;
 
     Document* doc = document();
-    CaptionUserPreferences* captionsUserPreferences = doc->page()->group().captionPreferences();
-    Vector<RefPtr<TextTrack> > tracksForMenu = captionsUserPreferences->sortedTrackListForMenu(trackList);
+    if (!document()->page())
+        return;
+    CaptionUserPreferences* captionPreferences = document()->page()->group().captionPreferences();
+    Vector<RefPtr<TextTrack> > tracksForMenu = captionPreferences->sortedTrackListForMenu(trackList);
 
     RefPtr<Element> captionsHeader = doc->createElement(h3Tag, ASSERT_NO_EXCEPTION);
     captionsHeader->appendChild(doc->createTextNode(textTrackSubtitlesText()));
     appendChild(captionsHeader);
     RefPtr<Element> captionsMenuList = doc->createElement(ulTag, ASSERT_NO_EXCEPTION);
 
-    RefPtr<Element> menuItem;
-    menuItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
-    menuItem->appendChild(doc->createTextNode(textTrackOffText()));
-    menuItem->setAttribute(trackIndexAttributeName(), textTracksOffAttrValue, ASSERT_NO_EXCEPTION);
-    captionsMenuList->appendChild(menuItem);
-    m_menuItems.append(menuItem);
-
     for (unsigned i = 0, length = tracksForMenu.size(); i < length; ++i) {
         RefPtr<TextTrack> textTrack = tracksForMenu[i];
-        menuItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
-
-        // Add a custom attribute to the <li> element which will allow
-        // us to easily associate the user tapping here with the
-        // track. Since this list is rebuilt if the tracks change, we
-        // should always be in sync.
-        menuItem->setAttribute(trackIndexAttributeName(), String::number(i), ASSERT_NO_EXCEPTION);
-
-        menuItem->appendChild(doc->createTextNode(captionsUserPreferences->displayNameForTrack(textTrack.get())));
-
+        RefPtr<Element> menuItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+        menuItem->appendChild(doc->createTextNode(captionPreferences->displayNameForTrack(textTrack.get())));
         captionsMenuList->appendChild(menuItem);
         m_menuItems.append(menuItem);
         m_menuToTrackMap.add(menuItem, textTrack);
     }
 
     appendChild(captionsMenuList);
-
-    updateDisplay();
 #endif
 }
 
index 19f83ce..6197896 100644 (file)
@@ -307,7 +307,6 @@ public:
     virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
 
     void updateDisplay();
-    void resetTrackListMenu() { m_trackListHasChanged = true; }
 
 private:
     MediaControlClosedCaptionsTrackListElement(Document*, MediaControls*);
@@ -324,7 +323,6 @@ private:
     MenuItemToTrackMap m_menuToTrackMap;
 #endif
     MediaControls* m_controls;
-    bool m_trackListHasChanged;
 };
 
 // ----------------------------
index 91a8a77..5adeca6 100644 (file)
@@ -305,8 +305,9 @@ void MediaControlsApple::makeTransparent()
 void MediaControlsApple::changedClosedCaptionsVisibility()
 {
     MediaControls::changedClosedCaptionsVisibility();
-    if (m_closedCaptionsTrackList)
-        m_closedCaptionsTrackList->resetTrackListMenu();
+    if (m_closedCaptionsContainer && m_closedCaptionsContainer->isShowing())
+        m_closedCaptionsContainer->hide();
+
 }
 
 void MediaControlsApple::reset()
@@ -340,11 +341,9 @@ void MediaControlsApple::reset()
         m_volumeSlider->setVolume(m_mediaController->volume());
 
     if (m_toggleClosedCaptionsButton) {
-        if (m_mediaController->hasClosedCaptions()) {
+        if (m_mediaController->hasClosedCaptions())
             m_toggleClosedCaptionsButton->show();
-            if (m_closedCaptionsTrackList)
-                m_closedCaptionsTrackList->resetTrackListMenu();
-        } else
+        else
             m_toggleClosedCaptionsButton->hide();
     }
 
@@ -510,8 +509,6 @@ void MediaControlsApple::toggleClosedCaptionTrackList()
 
 void MediaControlsApple::closedCaptionTracksChanged()
 {
-    if (m_closedCaptionsTrackList)
-        m_closedCaptionsTrackList->resetTrackListMenu();
     if (m_toggleClosedCaptionsButton) {
         if (m_mediaController->hasClosedCaptions())
             m_toggleClosedCaptionsButton->show();
index 97a665c..3c274b1 100644 (file)
@@ -95,6 +95,18 @@ const AtomicString& TextTrack::showingKeyword()
     return ended;
 }
 
+TextTrack* TextTrack::captionMenuOffItem()
+{
+    DEFINE_STATIC_LOCAL(RefPtr<TextTrack>, off, (TextTrack::create(0, 0, "off menu item", "", "")));
+    return off.get();
+}
+
+TextTrack* TextTrack::captionMenuAutomaticItem()
+{
+    DEFINE_STATIC_LOCAL(RefPtr<TextTrack>, automatic, (TextTrack::create(0, 0, "automatic menu item", "", "")));
+    return automatic.get();
+}
+
 TextTrack::TextTrack(ScriptExecutionContext* context, TextTrackClient* client, const AtomicString& kind, const AtomicString& label, const AtomicString& language, TextTrackType type)
     : TrackBase(context, TrackBase::TextTrack)
     , m_cues(0)
index c60d782..576945b 100644 (file)
@@ -73,6 +73,9 @@ public:
     }
     virtual ~TextTrack();
 
+    static TextTrack* captionMenuOffItem();
+    static TextTrack* captionMenuAutomaticItem();
+
     void setMediaElement(HTMLMediaElement* element) { m_mediaElement = element; }
     HTMLMediaElement* mediaElement() { return m_mediaElement; }
 
index d231058..a9ec307 100644 (file)
@@ -38,10 +38,10 @@ namespace WebCore {
 
 CaptionUserPreferences::CaptionUserPreferences(PageGroup* group)
     : m_pageGroup(group)
+    , m_displayMode(AlwaysOn)
     , m_timer(this, &CaptionUserPreferences::timerFired)
     , m_testingMode(false)
     , m_havePreferences(false)
-    , m_shouldShowCaptions(false)
 {
 }
 
@@ -49,14 +49,6 @@ CaptionUserPreferences::~CaptionUserPreferences()
 {
 }
 
-bool CaptionUserPreferences::shouldShowCaptions() const
-{
-    if (!m_testingMode)
-        return false;
-    
-    return m_shouldShowCaptions || userPrefersCaptions() || userPrefersSubtitles();
-}
-
 void CaptionUserPreferences::timerFired(Timer<CaptionUserPreferences>*)
 {
     captionPreferencesChanged();
@@ -72,10 +64,15 @@ void CaptionUserPreferences::notify()
         m_timer.startOneShot(0);
 }
 
-void CaptionUserPreferences::setShouldShowCaptions(bool preference)
+CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferences::captionDisplayMode() const
 {
-    m_shouldShowCaptions = preference;
-    if (m_testingMode && !preference) {
+    return m_displayMode;
+}
+
+void CaptionUserPreferences::setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode mode)
+{
+    m_displayMode = mode;
+    if (m_testingMode && mode != AlwaysOn) {
         setUserPrefersCaptions(false);
         setUserPrefersSubtitles(false);
     }
@@ -153,7 +150,7 @@ Vector<String> CaptionUserPreferences::preferredLanguages() const
     return languages;
 }
 
-void CaptionUserPreferences::setPreferredLanguage(String language)
+void CaptionUserPreferences::setPreferredLanguage(const String& language)
 {
     m_userPreferredLanguage = language;
     notify();
@@ -198,28 +195,30 @@ int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track, HTMLMediaE
 
     if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
         return trackScore;
-
+    
+    if (!userPrefersSubtitles() && !userPrefersCaptions())
+        return trackScore;
+    
     if (track->kind() == TextTrack::subtitlesKeyword() && userPrefersSubtitles())
         trackScore = 1;
     else if (track->kind() == TextTrack::captionsKeyword() && userPrefersCaptions())
         trackScore = 1;
     
-    return trackScore + textTrackLanguageSelectionScore(track);
+    return trackScore + textTrackLanguageSelectionScore(track, preferredLanguages());
 }
 
-int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track) const
+int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track, const Vector<String>& preferredLanguages) const
 {
     if (track->language().isEmpty())
         return 0;
 
-    Vector<String> languages = preferredLanguages();
-    size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), languages);
-    if (languageMatchIndex >= languages.size())
+    size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), preferredLanguages);
+    if (languageMatchIndex >= preferredLanguages.size())
         return 0;
 
     // Matching a track language is more important than matching track type, so this multiplier must be
     // greater than the maximum value returned by textTrackSelectionScore.
-    return (languages.size() - languageMatchIndex) * 10;
+    return (preferredLanguages.size() - languageMatchIndex) * 10;
 }
 
 }
index b7c1aeb..6ca36da 100644 (file)
@@ -46,12 +46,16 @@ public:
     static PassOwnPtr<CaptionUserPreferences> create(PageGroup* group) { return adoptPtr(new CaptionUserPreferences(group)); }
     virtual ~CaptionUserPreferences();
 
-    virtual bool userHasCaptionPreferences() const { return m_testingMode && m_havePreferences; }
-    virtual bool shouldShowCaptions() const;
-    virtual void setShouldShowCaptions(bool);
+    enum CaptionDisplayMode {
+        Automatic,
+        ForcedOnly,
+        AlwaysOn
+    };
+    virtual CaptionDisplayMode captionDisplayMode() const;
+    virtual void setCaptionDisplayMode(CaptionDisplayMode);
 
     virtual int textTrackSelectionScore(TextTrack*, HTMLMediaElement*) const;
-    virtual int textTrackLanguageSelectionScore(TextTrack*) const;
+    virtual int textTrackLanguageSelectionScore(TextTrack*, const Vector<String>&) const;
 
     virtual bool userPrefersCaptions() const;
     virtual void setUserPrefersCaptions(bool);
@@ -69,7 +73,7 @@ public:
 
     virtual void captionPreferencesChanged();
 
-    virtual void setPreferredLanguage(String);
+    virtual void setPreferredLanguage(const String&);
     virtual Vector<String> preferredLanguages() const;
 
     virtual String displayNameForTrack(TextTrack*) const;
@@ -77,7 +81,7 @@ public:
 
     virtual bool testingMode() const { return m_testingMode; }
     virtual void setTestingMode(bool override) { m_testingMode = override; }
-
+    
     PageGroup* pageGroup() const { return m_pageGroup; }
 
 protected:
@@ -88,11 +92,11 @@ private:
     void notify();
 
     PageGroup* m_pageGroup;
+    CaptionDisplayMode m_displayMode;
     Timer<CaptionUserPreferences> m_timer;
     String m_userPreferredLanguage;
     bool m_testingMode;
     bool m_havePreferences;
-    bool m_shouldShowCaptions;
 };
     
 }
index ad234ca..bca57c6 100644 (file)
@@ -41,9 +41,8 @@ public:
     virtual ~CaptionUserPreferencesMac();
 
 #if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
-    virtual bool userHasCaptionPreferences() const OVERRIDE;
-    virtual bool shouldShowCaptions() const OVERRIDE;
-    virtual void setShouldShowCaptions(bool) OVERRIDE;
+    virtual CaptionDisplayMode captionDisplayMode() const OVERRIDE;
+    virtual void setCaptionDisplayMode(CaptionDisplayMode) OVERRIDE;
 
     virtual bool userPrefersCaptions() const OVERRIDE;
     virtual bool userPrefersSubtitles() const OVERRIDE;
@@ -53,7 +52,7 @@ public:
 
     virtual void setInterestedInCaptionPreferenceChanges() OVERRIDE;
 
-    virtual void setPreferredLanguage(String) OVERRIDE;
+    virtual void setPreferredLanguage(const String&) OVERRIDE;
     virtual Vector<String> preferredLanguages() const OVERRIDE;
 
     virtual void captionPreferencesChanged() OVERRIDE;
index 4a6e5ea..fd82a81 100644 (file)
@@ -60,8 +60,8 @@
 
 SOFT_LINK_FRAMEWORK_OPTIONAL(MediaAccessibility)
 
-SOFT_LINK(MediaAccessibility, MACaptionAppearanceGetShowCaptions, bool, (MACaptionAppearanceDomain domain), (domain))
-SOFT_LINK(MediaAccessibility, MACaptionAppearanceSetShowCaptions, void, (MACaptionAppearanceDomain domain, bool showCaptions), (domain, showCaptions))
+SOFT_LINK(MediaAccessibility, MACaptionAppearanceGetDisplayType, MACaptionAppearanceDisplayType, (MACaptionAppearanceDomain domain), (domain))
+SOFT_LINK(MediaAccessibility, MACaptionAppearanceSetDisplayType, void, (MACaptionAppearanceDomain domain, MACaptionAppearanceDisplayType displayType), (domain, displayType))
 SOFT_LINK(MediaAccessibility, MACaptionAppearanceCopyForegroundColor, CGColorRef, (MACaptionAppearanceDomain domain, MACaptionAppearanceBehavior *behavior), (domain, behavior))
 SOFT_LINK(MediaAccessibility, MACaptionAppearanceCopyBackgroundColor, CGColorRef, (MACaptionAppearanceDomain domain, MACaptionAppearanceBehavior *behavior), (domain, behavior))
 SOFT_LINK(MediaAccessibility, MACaptionAppearanceCopyWindowColor, CGColorRef, (MACaptionAppearanceDomain domain, MACaptionAppearanceBehavior *behavior), (domain, behavior))
@@ -116,22 +116,54 @@ CaptionUserPreferencesMac::~CaptionUserPreferencesMac()
 
 #if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
 
-bool CaptionUserPreferencesMac::shouldShowCaptions() const
+CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferencesMac::captionDisplayMode() const
 {
     if (testingMode() || !MediaAccessibilityLibrary())
-        return CaptionUserPreferences::shouldShowCaptions();
+        return CaptionUserPreferences::captionDisplayMode();
 
-    return MACaptionAppearanceGetShowCaptions(kMACaptionAppearanceDomainUser);
-}
+    MACaptionAppearanceDisplayType displayType = MACaptionAppearanceGetDisplayType(kMACaptionAppearanceDomainUser);
+    switch (displayType) {
+    case kMACaptionAppearanceDisplayTypeForcedOnly:
+        return ForcedOnly;
+        break;
+
+    case kMACaptionAppearanceDisplayTypeAutomatic:
+        return Automatic;
+        break;
+
+    case kMACaptionAppearanceDisplayTypeAlwaysOn:
+        return AlwaysOn;
+        break;
+    }
 
-void CaptionUserPreferencesMac::setShouldShowCaptions(bool preference)
+    ASSERT_NOT_REACHED();
+    return ForcedOnly;
+}
+    
+void CaptionUserPreferencesMac::setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode mode)
 {
     if (testingMode() || !MediaAccessibilityLibrary()) {
-        CaptionUserPreferences::setShouldShowCaptions(preference);
+        CaptionUserPreferences::setCaptionDisplayMode(mode);
         return;
     }
 
-    MACaptionAppearanceSetShowCaptions(kMACaptionAppearanceDomainUser, preference);
+    MACaptionAppearanceDisplayType displayType = kMACaptionAppearanceDisplayTypeForcedOnly;
+    switch (mode) {
+        case Automatic:
+            displayType = kMACaptionAppearanceDisplayTypeAutomatic;
+            break;
+        case ForcedOnly:
+            displayType = kMACaptionAppearanceDisplayTypeForcedOnly;
+            break;
+        case AlwaysOn:
+            displayType = kMACaptionAppearanceDisplayTypeAlwaysOn;
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+    }
+
+    MACaptionAppearanceSetDisplayType(kMACaptionAppearanceDomainUser, displayType);
 }
 
 bool CaptionUserPreferencesMac::userPrefersCaptions() const
@@ -154,14 +186,6 @@ bool CaptionUserPreferencesMac::userPrefersSubtitles() const
     return !(captioningMediaCharacteristics && CFArrayGetCount(captioningMediaCharacteristics.get()));
 }
 
-bool CaptionUserPreferencesMac::userHasCaptionPreferences() const
-{
-    if (testingMode() || !MediaAccessibilityLibrary())
-        return CaptionUserPreferences::userHasCaptionPreferences();
-
-    return true;
-}
-
 void CaptionUserPreferencesMac::setInterestedInCaptionPreferenceChanges()
 {
     if (!MediaAccessibilityLibrary())
@@ -481,7 +505,7 @@ void CaptionUserPreferencesMac::updateCaptionStyleSheetOveride()
              Vector<String>(), InjectInAllFrames, UserStyleAuthorLevel, InjectInExistingDocuments);
 }
 
-void CaptionUserPreferencesMac::setPreferredLanguage(String language)
+void CaptionUserPreferencesMac::setPreferredLanguage(const String& language)
 {
     if (testingMode() || !MediaAccessibilityLibrary()) {
         CaptionUserPreferences::setPreferredLanguage(language);
@@ -525,9 +549,30 @@ Vector<String> CaptionUserPreferencesMac::preferredLanguages() const
     return userPreferredLanguages;
 }
 #endif  // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
-    
+
+static String languageIdentifier(const String& languageCode)
+{
+    if (languageCode.isEmpty())
+        return languageCode;
+
+    String lowercaseLanguageCode = languageCode.lower();
+
+    // Need 2U here to disambiguate String::operator[] from operator(NSString*, int)[] in a production build.
+    if (lowercaseLanguageCode.length() >= 3 && (lowercaseLanguageCode[2U] == '_' || lowercaseLanguageCode[2U] == '-'))
+        lowercaseLanguageCode.truncate(2);
+
+    return lowercaseLanguageCode;
+}
+
 static String trackDisplayName(TextTrack* track)
 {
+    if (track == TextTrack::captionMenuOffItem())
+        return textTrackOffMenuItemText();
+    if (track == TextTrack::captionMenuAutomaticItem()) {
+        String preferredLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(defaultLanguage()));
+        return textTrackAutomaticMenuItemText(preferredLanguageDisplayName);
+    }
+
     StringBuilder displayName;
     String label = track->label();
     String trackLanguageIdentifier = track->language();
@@ -587,23 +632,12 @@ String CaptionUserPreferencesMac::displayNameForTrack(TextTrack* track) const
     return trackDisplayName(track);
 }
 
-static String languageIdentifier(const String& languageCode)
-{
-    if (languageCode.isEmpty())
-        return languageCode;
-
-    String lowercaseLanguageCode = languageCode.lower();
-
-    // Need 2U here to disambiguate String::operator[] from operator(NSString*, int)[] in a production build.
-    if (lowercaseLanguageCode.length() >= 3 && (lowercaseLanguageCode[2U] == '_' || lowercaseLanguageCode[2U] == '-'))
-        lowercaseLanguageCode.truncate(2);
-    
-    return lowercaseLanguageCode;
-}
-
 int CaptionUserPreferencesMac::textTrackSelectionScore(TextTrack* track, HTMLMediaElement* mediaElement) const
 {
-    if (!shouldShowCaptions() && !mediaElement->webkitClosedCaptionsVisible())
+    CaptionDisplayMode displayMode = captionDisplayMode();
+    if (displayMode == ForcedOnly)
+        return 0;
+    if (displayMode == AlwaysOn && (!userPrefersSubtitles() && !userPrefersCaptions()))
         return 0;
     if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
         return 0;
@@ -612,6 +646,28 @@ int CaptionUserPreferencesMac::textTrackSelectionScore(TextTrack* track, HTMLMed
     if (!track->isMainProgramContent())
         return 0;
 
+    Vector<String> userPreferredCaptionLanguages = preferredLanguages();
+
+    if (displayMode == Automatic) {
+
+        // Only enable a text track if the current audio track is not in the user's preferred language.
+        if (!mediaElement || !mediaElement->player())
+            return 0;
+
+        String audioTrackLanguage = mediaElement->player()->languageOfPrimaryAudioTrack();
+
+        if (audioTrackLanguage.isEmpty())
+            return 0;
+
+        Vector<String> languages;
+        languages.append(defaultLanguage());
+        size_t offset = indexOfBestMatchingLanguageInList(audioTrackLanguage, languages);
+        if (!offset)
+            return 0;
+
+        userPreferredCaptionLanguages = languages;
+    }
+
     int trackScore = 0;
 
     if (userPrefersCaptions()) {
@@ -632,7 +688,7 @@ int CaptionUserPreferencesMac::textTrackSelectionScore(TextTrack* track, HTMLMed
             trackScore = 1;
     }
 
-    return trackScore + textTrackLanguageSelectionScore(track);
+    return trackScore + textTrackLanguageSelectionScore(track, userPreferredCaptionLanguages);
 }
 
 static bool textTrackCompare(const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b)
@@ -738,6 +794,9 @@ Vector<RefPtr<TextTrack> > CaptionUserPreferencesMac::sortedTrackListForMenu(Tex
 
     nonCopyingSort(tracksForMenu.begin(), tracksForMenu.end(), textTrackCompare);
 
+    tracksForMenu.insert(0, TextTrack::captionMenuOffItem());
+    tracksForMenu.insert(1, TextTrack::captionMenuAutomaticItem());
+
     return tracksForMenu;
 }
     
index 2931b83..a32cd81 100644 (file)
@@ -1025,11 +1025,16 @@ String textTrackSubtitlesText()
     return WEB_UI_STRING("Subtitles", "Menu section heading for subtitles");
 }
 
-String textTrackOffText()
+String textTrackOffMenuItemText()
 {
     return WEB_UI_STRING("Off", "Menu item label for the track that represents disabling closed captions");
 }
 
+String textTrackAutomaticMenuItemText(const String& language)
+{
+    return formatLocalizedString(WEB_UI_STRING("Automatic (%@)", "Menu item label for automatic track selection behavior in the form of 'Automatic (SystemLanguage)'"), language.createCFString().get());
+}
+
 String textTrackNoLabelText()
 {
     return WEB_UI_STRING_KEY("Unknown", "Unknown (text track)", "Menu item label for a text track that has no other name");
index ec4be3b..1823fab 100644 (file)
@@ -267,7 +267,8 @@ namespace WebCore {
 
 #if ENABLE(VIDEO_TRACK)
     String textTrackSubtitlesText();
-    String textTrackOffText();
+    String textTrackOffMenuItemText();
+    String textTrackAutomaticMenuItemText(const String& language);
     String textTrackNoLabelText();
 #if PLATFORM(MAC)
     String textTrackCountryAndLanguageMenuItemText(const String& title, const String& country, const String& language);
index 89c5ba5..12576fb 100644 (file)
@@ -1154,6 +1154,13 @@ void MediaPlayer::simulateAudioInterruption()
 }
 #endif
 
+String MediaPlayer::languageOfPrimaryAudioTrack() const
+{
+    if (!m_private)
+        return emptyString();
+    
+    return m_private->languageOfPrimaryAudioTrack();
+}
 }
 
 #endif
index 9f1dda3..e17ad6c 100644 (file)
@@ -466,6 +466,8 @@ public:
     void simulateAudioInterruption();
 #endif
 
+    String languageOfPrimaryAudioTrack() const;
+
 private:
     MediaPlayer(MediaPlayerClient*);
     void loadWithNextMediaEngine(MediaPlayerFactory*);
index b79709d..d22c93a 100644 (file)
@@ -191,6 +191,8 @@ public:
 #if USE(GSTREAMER)
     virtual void simulateAudioInterruption() { }
 #endif
+    
+    virtual String languageOfPrimaryAudioTrack() const { return emptyString(); }
 };
 
 }
index 9d4eb64..f70479a 100644 (file)
@@ -159,6 +159,8 @@ private:
     virtual MediaPlayer::MediaKeyException cancelKeyRequest(const String&, const String&);
 #endif
 
+    virtual String languageOfPrimaryAudioTrack() const OVERRIDE;
+
 #if HAVE(AVFOUNDATION_TEXT_TRACK_SUPPORT)
     virtual void setCurrentTrack(InbandTextTrackPrivateAVF*) OVERRIDE;
     virtual InbandTextTrackPrivateAVF* currentTrack() OVERRIDE;
@@ -172,6 +174,7 @@ private:
     RetainPtr<AVPlayerLayer> m_videoLayer;
     RetainPtr<WebCoreAVFMovieObserver> m_objcObserver;
     RetainPtr<id> m_timeObserver;
+    mutable String m_languageOfPrimaryAudioTrack;
     bool m_videoFrameHasDrawn;
     bool m_haveCheckedPlayability;
 
index cb5c210..b2eb855 100644 (file)
@@ -946,6 +946,8 @@ float MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(float timeValue)
 
 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
 {
+    m_languageOfPrimaryAudioTrack = String();
+
     if (!m_avAsset)
         return;
 
@@ -1377,6 +1379,45 @@ InbandTextTrackPrivateAVF* MediaPlayerPrivateAVFoundationObjC::currentTrack()
 }
 #endif // HAVE(AVFOUNDATION_TEXT_TRACK_SUPPORT)
 
+String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
+{
+    if (!m_languageOfPrimaryAudioTrack.isNull())
+        return m_languageOfPrimaryAudioTrack;
+
+    if (!m_avPlayerItem.get())
+        return emptyString();
+
+#if HAVE(AVFOUNDATION_TEXT_TRACK_SUPPORT)
+    // If AVFoundation has an audible group, return the language of the currently selected audible option.
+    AVMediaSelectionGroupType *audibleGroup = [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
+    AVMediaSelectionOptionType *currentlySelectedAudibleOption = [m_avPlayerItem.get() selectedMediaOptionInMediaSelectionGroup:audibleGroup];
+    if (currentlySelectedAudibleOption) {
+        m_languageOfPrimaryAudioTrack = [[currentlySelectedAudibleOption locale] localeIdentifier];
+        return m_languageOfPrimaryAudioTrack;
+    }
+#endif // HAVE(AVFOUNDATION_TEXT_TRACK_SUPPORT)
+
+    // AVFoundation synthesizes an audible group when there is only one ungrouped audio track if there is also a legible group (one or
+    // more in-band text tracks). It doesn't know about out-of-band tracks, so if there is a single audio track return its language.
+    NSArray *tracks = [m_avAsset.get() tracksWithMediaType:AVMediaTypeAudio];
+    if (!tracks || [tracks count] != 1) {
+        m_languageOfPrimaryAudioTrack = emptyString();
+        return m_languageOfPrimaryAudioTrack;
+    }
+
+    AVAssetTrack *track = [tracks objectAtIndex:0];
+    NSString *language = [track extendedLanguageTag];
+
+    // Some legacy tracks have "und" as a language, treat that the same as no language at all.
+    if (language && ![language isEqualToString:@"und"]) {
+        m_languageOfPrimaryAudioTrack = language;
+        return m_languageOfPrimaryAudioTrack;
+    }
+
+    m_languageOfPrimaryAudioTrack = emptyString();
+    return m_languageOfPrimaryAudioTrack;
+}
+    
 NSArray* assetMetadataKeyNames()
 {
     static NSArray* keys;