Allow ports specific text track menu
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Mar 2013 20:13:36 +0000 (20:13 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Mar 2013 20:13:36 +0000 (20:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=112800

Reviewed by Dean Jackson.

Source/WebCore:

No new tests, existing tests updated for changes.

* English.lproj/Localizable.strings: Add localizable strings for text track names.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement): userPrefersCaptions -> shouldShowCaptions.
(WebCore::HTMLMediaElement::setSelectedTextTrack): toggleTrackAtIndex -> setSelectedTextTrack.
(WebCore::HTMLMediaElement::userPrefersCaptions): userPrefersCaptions -> shouldShowCaptions.
(WebCore::HTMLMediaElement::userIsInterestedInThisTrackKind): Get preferences from CaptionUserPreferences
    instead of from Settings.
(WebCore::HTMLMediaElement::setSelectedTextTrack): Renamed from toggleTrackAtIndex. Now takes
    a TextTrack* instead of an int.
(WebCore::HTMLMediaElement::captionPreferencesChanged): Don't force a recalculation unless
    captions are enabled/disabled to avoid unnecessary thrash.
* html/HTMLMediaElement.h:

* html/shadow/MediaControlElements.cpp:
(WebCore::MediaControlClosedCaptionsTrackListElement::defaultEventHandler): Use the menu to track
    map instead of just the track index.
(WebCore::MediaControlClosedCaptionsTrackListElement::updateDisplay): Build and configure the
    menu from the menu to track map.
(WebCore::MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu): Start with a list
    of tracks already sorted according to platform specific rules. Build a menu to track map
    so tracks are retained while the menu is visible.
* html/shadow/MediaControlElements.h:

* html/shadow/MediaControls.cpp:
(WebCore::MediaControls::textTrackPreferencesChanged): Call closedCaptionTracksChanged so the
    track menu will be rebuilt before it is shown again.

* html/track/InbandTextTrack.cpp:
(WebCore::InbandTextTrack::containsOnlyForcedSubtitles): New, passthrough to the private track.
(WebCore::InbandTextTrack::isMainProgramContent): Ditto.
(WebCore::InbandTextTrack::isEasyToRead): Ditto.
* html/track/InbandTextTrack.h:
* html/track/TextTrack.h:

* html/track/TextTrackList.cpp:
(WebCore::TextTrackList::contains): New.
* html/track/TextTrackList.h:

* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::CaptionUserPreferences): Pull in from .h ditto.
(WebCore::CaptionUserPreferences::~CaptionUserPreferences): Ditto.
(WebCore::CaptionUserPreferences::shouldShowCaptions): Renamed from userPrefersCaptions.
(WebCore::CaptionUserPreferences::setShouldShowCaptions): New.
(WebCore::CaptionUserPreferences::userPrefersCaptions): Ditto.
(WebCore::CaptionUserPreferences::setUserPrefersCaptions): Ditto.
(WebCore::CaptionUserPreferences::userPrefersSubtitles): Ditto.
(WebCore::CaptionUserPreferences::setUserPrefersSubtitles): Ditto.
(WebCore::CaptionUserPreferences::userPrefersTextDescriptions): Ditto.
(WebCore::CaptionUserPreferences::setUserPrefersTextDescriptions): Ditto.
(WebCore::CaptionUserPreferences::displayNameForTrack): Ditto.
(WebCore::CaptionUserPreferences::sortedTrackListForMenu): Ditto.
* page/CaptionUserPreferences.h:

* page/CaptionUserPreferencesMac.h:
* page/CaptionUserPreferencesMac.mm:
(WebCore::CaptionUserPreferencesMac::shouldShowCaptions): Renamed from userPrefersCaptions.
(WebCore::CaptionUserPreferencesMac::userPrefersCaptions): New.
(WebCore::CaptionUserPreferences::userPrefersSubtitles): Ditto.
(WebCore::trackDisplayName): Update logic used to build track title.
(WebCore::CaptionUserPreferencesMac::displayNameForTrack): Call trackDisplayName.
(WebCore::textTrackCompare): Text track title sort comparison function.
(WebCore::CaptionUserPreferencesMac::sortedTrackListForMenu): New. Sort the list of tracks
    according to title, language, and user preferences.

* platform/Language.cpp:
(WebCore::displayNameForLanguageLocale): Don't leak the CFLocale.

* platform/LocalizedStrings.cpp:
(WebCore::textTrackCountryAndLanguageMenuItemText): New.
(WebCore::textTrackLanguageMenuItemText): Ditto.
(WebCore::closedCaptionTrackMenuItemText): Ditto.
(WebCore::sdhTrackMenuItemText): Ditto.
(WebCore::easyReaderTrackMenuItemText): Ditto.
* platform/LocalizedStrings.h:

* platform/graphics/InbandTextTrackPrivate.h:
* platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.mm:
(WebCore::InbandTextTrackPrivateAVFObjC::containsOnlyForcedSubtitles): New, return AVFoundation property.
(WebCore::InbandTextTrackPrivateAVFObjC::isMainProgramContent): Ditto.
(WebCore::InbandTextTrackPrivateAVFObjC::isEasyToRead): Ditto.

* testing/InternalSettings.cpp:
(WebCore::InternalSettings::setShouldDisplayTrackKind): Set setting via CaptionUserPreferences
    instead of from Settings.
(WebCore::InternalSettings::shouldDisplayTrackKind): Get setting from CaptionUserPreferences
    instead of from Settings.

Source/WebKit:

* WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in: Add new exports.
* win/WebKit.vcproj/WebKitExports.def.in: Ditto.

LayoutTests:

* media/track/track-user-preferences-expected.txt:
* media/track/track-user-preferences.html:
* media/video-controls-captions-trackmenu-localized.html:
* media/video-controls-captions-trackmenu-sorted.html:
* media/video-controls-captions-trackmenu.html:
* platform/mac/media/video-controls-captions-trackmenu-expected.txt:
* platform/mac/media/video-controls-captions-trackmenu-localized-expected.txt:
* platform/mac/media/video-controls-captions-trackmenu-sorted-expected.txt:

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

36 files changed:
LayoutTests/ChangeLog
LayoutTests/media/track/track-user-preferences-expected.txt
LayoutTests/media/track/track-user-preferences.html
LayoutTests/media/video-controls-captions-trackmenu-localized.html
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-localized-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/MediaControlElements.cpp
Source/WebCore/html/shadow/MediaControlElements.h
Source/WebCore/html/shadow/MediaControls.cpp
Source/WebCore/html/track/InbandTextTrack.cpp
Source/WebCore/html/track/InbandTextTrack.h
Source/WebCore/html/track/TextTrack.cpp
Source/WebCore/html/track/TextTrack.h
Source/WebCore/html/track/TextTrackList.cpp
Source/WebCore/html/track/TextTrackList.h
Source/WebCore/page/CaptionUserPreferences.cpp
Source/WebCore/page/CaptionUserPreferences.h
Source/WebCore/page/CaptionUserPreferencesMac.h
Source/WebCore/page/CaptionUserPreferencesMac.mm
Source/WebCore/platform/Language.cpp
Source/WebCore/platform/LocalizedStrings.cpp
Source/WebCore/platform/LocalizedStrings.h
Source/WebCore/platform/graphics/InbandTextTrackPrivate.h
Source/WebCore/platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.mm
Source/WebCore/testing/InternalSettings.cpp
Source/WebKit/ChangeLog
Source/WebKit/WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in
Source/WebKit/win/WebKit.vcproj/WebKitExports.def.in

index 0da2672..1dea92c 100644 (file)
@@ -1,3 +1,19 @@
+2013-03-20  Eric Carlson  <eric.carlson@apple.com>
+
+        Allow ports specific text track menu
+        https://bugs.webkit.org/show_bug.cgi?id=112800
+
+        Reviewed by Dean Jackson.
+
+        * media/track/track-user-preferences-expected.txt:
+        * media/track/track-user-preferences.html:
+        * media/video-controls-captions-trackmenu-localized.html:
+        * media/video-controls-captions-trackmenu-sorted.html:
+        * media/video-controls-captions-trackmenu.html:
+        * platform/mac/media/video-controls-captions-trackmenu-expected.txt:
+        * platform/mac/media/video-controls-captions-trackmenu-localized-expected.txt:
+        * platform/mac/media/video-controls-captions-trackmenu-sorted-expected.txt:
+
 2013-03-20  Julien Chaffraix  <jchaffraix@webkit.org>
 
         [CSS Grid Layout] Improper repainting when grid item change their position
index 1561261..9fe1feb 100644 (file)
@@ -18,10 +18,10 @@ EXPECTED (event.target.readyState == '2') OK
 
 Test 3: select 'fr' track from menu
 - show captions menu.
-EXPECTED (trackMenuItems[1].className == '') OK
-EXPECTED (trackMenuItems[2].className == '') OK
-EXPECTED (trackMenuItems[3].className == 'selected') OK
-- click on menu item 2.
+EXPECTED (trackMenuItems[menuIndexForLanguage('French')].className == '') OK
+EXPECTED (trackMenuItems[menuIndexForLanguage('English')].className == '') OK
+EXPECTED (trackMenuItems[menuIndexForLanguage('Norwegian')].className == 'selected') OK
+- click on 'French' menu item.
 EVENT(load)
 EXPECTED (event.target.srclang == 'fr') OK
 EXPECTED (event.target.readyState == '2') OK
@@ -38,7 +38,7 @@ EXPECTED (video.textTracks[2].language == 'fr') OK
 
 Test 5: turning captions off from menu disables caption selection
 - show captions menu.
-- click on menu item 0.
+- click on 'Off' menu item.
 - creating tracks for: [ru,jp,en].
 EXPECTED (video.textTracks[0].mode == 'disabled') OK
 EXPECTED (video.textTracks[1].mode == 'disabled') OK
index 6a55a1a..cc359a3 100644 (file)
                 return trackListItems;
             }
 
-            function selectCaptionMenuItem(index)
+            function menuIndexForLanguage(language)
             {
-                consoleWrite("- click on menu item " + index + ".");
                 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;
@@ -70,7 +86,7 @@
             {
                 var track = document.createElement('track');
                 track.setAttribute('kind', "captions");
-                track.src = 'data:text/vtt,'+encodeURIComponent("WEBVTT\n\n00:00:00.000 --> 00:00:01.000\nCaption 1\n");
+                track.src = 'data:text/vtt,' + encodeURIComponent("WEBVTT\n\n00:00:00.000 --> 00:00:01.000\nCaption 1\n");
                 track.setAttribute('srclang', language);
                 track.setAttribute('onload', 'trackLoaded()');
                 if (isDefault) {
                     break;
 
                 case 3:
-                    // Clear the debug setting so it isn't considered in the track selection logic
-                    internals.settings.setShouldDisplayTrackKind('Captions', false);
-
                     consoleWrite("<br>Test 3: select 'fr' track from menu");
                     showCaptionMenu();
                     break;
 
                 case 4:
                     trackMenuItems = trackMenuList();
-                    testExpected("trackMenuItems[1].className", "");
-                    testExpected("trackMenuItems[2].className", "");
-                    testExpected("trackMenuItems[3].className", "selected");
+                    testExpected("trackMenuItems[menuIndexForLanguage('French')].className", "");
+                    testExpected("trackMenuItems[menuIndexForLanguage('English')].className", "");
+                    testExpected("trackMenuItems[menuIndexForLanguage('Norwegian')].className", "selected");
 
-                    selectCaptionMenuItem(2);
+                    selectCaptionMenuItem("French");
                     expectedLanguage = 'fr';
                     break;
 
                     break;
 
                 case 7:
-                    selectCaptionMenuItem(0);
+                    selectCaptionMenuItem("Off");
                     createTrackElements([ 'ru', 'jp', 'en']);
                     timer = setTimeout(nextStep, 100);
                     break;
index 5e494bc..80432f1 100644 (file)
@@ -44,8 +44,8 @@
             testExpected("item.textContent", "English CC");
 
             item = captionsEntries[2];
-            consoleWrite("<br>Third item in captions menu should be labelled 'Unknown'");
-            testExpected("item.textContent", "Unknown");
+            consoleWrite("<br>Third item in captions menu should be labelled 'Unknown SDH'");
+            testExpected("item.textContent", "Unknown SDH");
 
             item = captionsEntries[3];
             consoleWrite("<br>Fourth item in captions menu should be labelled 'Unknown'");
index e40dd5a..12f8e9e 100644 (file)
@@ -7,11 +7,16 @@
         <script src="video-test.js"></script>
         <script src="trackmenu-test.js"></script>
         <script>
+            var test = 0;
             if (window.testRunner)
                 testRunner.dumpAsText();
 
-            function testSortedMenu()
+            function textTrackMenuItems()
             {
+                // Show and hide the menu to force a recalculation.
+                clickCCButton();
+                clickCCButton();
+
                 var trackListElement = getTrackListElement();
                 if (!trackListElement)
                     return;
                     failTest("There should be a ul element in track list menu");
                     return;
                 }
-                var lastTrackLabel = null;
-                var trackListItems = trackListSection.querySelectorAll("li");
-                if (!trackListItems || trackListItems.length != 7) {
-                    failTest("There should be seven li elements in the menu");
-                    return;
-                }
-                for (var j = 0; j < trackListItems.length; j++) {
-                    var item = trackListItems[j];
-                    if (j == 0)
-                        logResult(item.textContent == "Off", "First item should be labelled off");
-                    else {
-                        if (lastTrackLabel)
-                            logResult(item.textContent > lastTrackLabel, lastTrackLabel + " comes before " + item.textContent);
-                        else
-                            logResult(true, item.textContent + " is the first real item in the list");
-                        lastTrackLabel = item.textContent;
-                    }
+                return trackListSection.querySelectorAll("li");
+            }
+
+            function testSortedMenu()
+            {
+                switch (test)
+                {
+                case 0:
+                    consoleWrite("<br><i>** Test with preferred language set to English, track type set to captions<" + "/i>");
+                    run("internals.settings.setShouldDisplayTrackKind('Captions', true)");
+                    window.setTimeout(testSortedMenu, 0);
+                    break;
+
+                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("video.textTracks.length", 6);
+                    testExpected("trackMenuItems.length", 7);
+
+                    consoleWrite("<br><i>** Change preferred language to Japanese, change preference to subtitles<" + "/i>");
+                    run("internals.setUserPreferredLanguages(['ja'])");
+                    run("internals.settings.setShouldDisplayTrackKind('Captions', false)");
+                    run("internals.settings.setShouldDisplayTrackKind('Subtitles', true)");
+
+                    window.setTimeout(testSortedMenu, 0);
+                break;
+
+                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("video.textTracks.length", 6);
+                    testExpected("trackMenuItems.length", 4);
+
+                    consoleWrite("");
+                    endTest();
+                break;
                 }
-                endTest();
+
+                ++test;
             }
 
             function start()
     </head>
 
     <body onload="start()">
-        <p>Test that captions and subtitles are sorted in the menu.</p>
+        <p>Test that captions and subtitles are sorted in the menu according to user preferences.</p>
         <video width="500" height="300" controls>
-            <track label="c" kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="ja">
-            <track label="b" kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="en-au">
-            <track label="a" kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="en">
-            <track label="b" kind="subtitles" src="track/captions-webvtt/captions-fast.vtt" srclang="ja">
-            <track label="a" kind="subtitles" src="track/captions-webvtt/captions-fast.vtt" srclang="en-au">
-            <track label="c" kind="subtitles" src="track/captions-webvtt/captions-fast.vtt" srclang="en">
+            <track label="z" kind="subtitles" src="track/captions-webvtt/captions-fast.vtt" srclang="ja">
+            <track label="y" kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="en-au">
+            <track label="x" kind="subtitles" src="track/captions-webvtt/captions-fast.vtt" srclang="en-au">
+            <track label="w" kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="ja">
+            <track label="v" kind="subtitles" src="track/captions-webvtt/captions-fast.vtt" srclang="en">
+            <track label="u" kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="en">
         </video>
     </body>
 </html>
index 383a3f0..90e76f1 100644 (file)
@@ -15,6 +15,7 @@
                 consoleWrite("<br>*** Add another text track.");
                 run('video.addTextTrack("captions", "Commentary", "ru")');
                 window.setTimeout(turnCaptionsOn, 100);
+                consoleWrite("");
             }
 
             function trackMenuList()
 
             function testMenu()
             {
-                var trackListItems = trackMenuList();
+                trackListItems = trackMenuList();
                 var expectedItemCount = video.textTracks.length + 1;
                 consoleWrite("There should be " + expectedItemCount + " items in the menu.");
-                testExpected(trackListItems.length, expectedItemCount);
+                testExpected("trackListItems.length", expectedItemCount);
             }
 
             function selectCaptionMenuItem(index, nextStep)
@@ -62,7 +63,7 @@
             function turnCaptionsOn()
             {
                 consoleWrite("*** Turning captions on");
-                // Click on the third item, which is the second track (Off is the first item in the menu)
+                // Click on the second item, which is the second track (Off is the first item in the menu)
                 selectCaptionMenuItem(2, testCaptionsVisible);
             }
 
     <body onload="start()">
         <p>Test that we are able to trigger the list of captions, and select from the list.</p>
         <video width="500" height="300" controls>
+            <track kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="fr">
             <track kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="en">
-            <track kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="en-au">
             <track kind="captions" src="track/captions-webvtt/captions-fast.vtt" srclang="ja">
         </video>
     </body>
index 00501bf..5527d3e 100644 (file)
@@ -7,9 +7,10 @@ RUN(internals.setUserPreferredLanguages(['en']))
 
 *** Add another text track.
 RUN(video.addTextTrack("captions", "Commentary", "ru"))
+
 *** Turning captions on
 There should be 5 items in the menu.
-EXPECTED (5 == '5') OK
+EXPECTED (trackListItems.length == '5') OK
 EXPECTED (video.textTracks.length == '4') OK
 Track 0 should be showing
 EXPECTED (video.textTracks[0].mode == 'showing') OK
@@ -25,7 +26,7 @@ EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Lorem') OK
 RUN(video.removeChild(document.querySelectorAll("track")[0]))
 *** Turning captions off
 There should be 4 items in the menu.
-EXPECTED (4 == '4') OK
+EXPECTED (trackListItems.length == '4') OK
 EXPECTED (video.textTracks.length == '3') OK
 Track 0 should be disabled
 EXPECTED (video.textTracks[0].mode == 'disabled') OK
index caf7a72..0c434c4 100644 (file)
@@ -11,10 +11,10 @@ First item in captions menu should be labelled 'Off'
 EXPECTED (item.textContent == 'Off') OK
 
 Second item in captions menu should be labelled 'English CC'
-EXPECTED (item.textContent == 'English CC'), OBSERVED 'Unknown' FAIL
+EXPECTED (item.textContent == 'English CC'), OBSERVED 'Unknown SDH' FAIL
 
-Third item in captions menu should be labelled 'Unknown'
-EXPECTED (item.textContent == 'Unknown') OK
+Third item in captions menu should be labelled 'Unknown SDH'
+EXPECTED (item.textContent == 'Unknown SDH'), OBSERVED 'Unknown' FAIL
 
 Fourth item in captions menu should be labelled 'Unknown'
 TypeError: 'undefined' is not an object (evaluating 'item.textContent')
index eafe9d1..99934a5 100644 (file)
@@ -1,15 +1,32 @@
-Test that captions and subtitles are sorted in the menu.
+Test that captions and subtitles are sorted in the menu according to user preferences.
 
 EVENT(canplaythrough)
 
 *** Set the user language preference.
 RUN(internals.setUserPreferredLanguages(['en']))
-First item should be labelled off OK
-a English (Australia) is the first real item in the list OK
-a English (Australia) comes before a English SDH OK
-a English SDH comes before b English (Australia) SDH OK
-b English (Australia) SDH comes before b Japanese OK
-b Japanese comes before c English OK
-c English comes before c Japanese SDH OK
+
+** 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 (video.textTracks.length == '6') OK
+EXPECTED (trackMenuItems.length == '7') 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 (video.textTracks.length == '6') OK
+EXPECTED (trackMenuItems.length == '4') OK
+
 END OF TEST
 
index 8831916..411cb7e 100644 (file)
@@ -1,3 +1,101 @@
+2013-03-20  Eric Carlson  <eric.carlson@apple.com>
+
+        Allow ports specific text track menu
+        https://bugs.webkit.org/show_bug.cgi?id=112800
+
+        Reviewed by Dean Jackson.
+
+        No new tests, existing tests updated for changes.
+
+        * English.lproj/Localizable.strings: Add localizable strings for text track names.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement): userPrefersCaptions -> shouldShowCaptions.
+        (WebCore::HTMLMediaElement::setSelectedTextTrack): toggleTrackAtIndex -> setSelectedTextTrack.
+        (WebCore::HTMLMediaElement::userPrefersCaptions): userPrefersCaptions -> shouldShowCaptions.
+        (WebCore::HTMLMediaElement::userIsInterestedInThisTrackKind): Get preferences from CaptionUserPreferences
+            instead of from Settings.
+        (WebCore::HTMLMediaElement::setSelectedTextTrack): Renamed from toggleTrackAtIndex. Now takes
+            a TextTrack* instead of an int.
+        (WebCore::HTMLMediaElement::captionPreferencesChanged): Don't force a recalculation unless 
+            captions are enabled/disabled to avoid unnecessary thrash.
+        * html/HTMLMediaElement.h:
+
+        * html/shadow/MediaControlElements.cpp:
+        (WebCore::MediaControlClosedCaptionsTrackListElement::defaultEventHandler): Use the menu to track
+            map instead of just the track index.
+        (WebCore::MediaControlClosedCaptionsTrackListElement::updateDisplay): Build and configure the
+            menu from the menu to track map.
+        (WebCore::MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu): Start with a list
+            of tracks already sorted according to platform specific rules. Build a menu to track map
+            so tracks are retained while the menu is visible.
+        * html/shadow/MediaControlElements.h:
+
+        * html/shadow/MediaControls.cpp:
+        (WebCore::MediaControls::textTrackPreferencesChanged): Call closedCaptionTracksChanged so the
+            track menu will be rebuilt before it is shown again.
+
+        * html/track/InbandTextTrack.cpp:
+        (WebCore::InbandTextTrack::containsOnlyForcedSubtitles): New, passthrough to the private track.
+        (WebCore::InbandTextTrack::isMainProgramContent): Ditto.
+        (WebCore::InbandTextTrack::isEasyToRead): Ditto.
+        * html/track/InbandTextTrack.h:
+        * html/track/TextTrack.h:
+
+        * html/track/TextTrackList.cpp:
+        (WebCore::TextTrackList::contains): New.
+        * html/track/TextTrackList.h:
+
+        * page/CaptionUserPreferences.cpp:
+        (WebCore::CaptionUserPreferences::CaptionUserPreferences): Pull in from .h ditto.
+        (WebCore::CaptionUserPreferences::~CaptionUserPreferences): Ditto.
+        (WebCore::CaptionUserPreferences::shouldShowCaptions): Renamed from userPrefersCaptions. 
+        (WebCore::CaptionUserPreferences::setShouldShowCaptions): New.
+        (WebCore::CaptionUserPreferences::userPrefersCaptions): Ditto.
+        (WebCore::CaptionUserPreferences::setUserPrefersCaptions): Ditto.
+        (WebCore::CaptionUserPreferences::userPrefersSubtitles): Ditto.
+        (WebCore::CaptionUserPreferences::setUserPrefersSubtitles): Ditto.
+        (WebCore::CaptionUserPreferences::userPrefersTextDescriptions): Ditto.
+        (WebCore::CaptionUserPreferences::setUserPrefersTextDescriptions): Ditto.
+        (WebCore::CaptionUserPreferences::displayNameForTrack): Ditto.
+        (WebCore::CaptionUserPreferences::sortedTrackListForMenu): Ditto.
+        * page/CaptionUserPreferences.h:
+
+        * page/CaptionUserPreferencesMac.h:
+        * page/CaptionUserPreferencesMac.mm:
+        (WebCore::CaptionUserPreferencesMac::shouldShowCaptions): Renamed from userPrefersCaptions. 
+        (WebCore::CaptionUserPreferencesMac::userPrefersCaptions): New.
+        (WebCore::CaptionUserPreferences::userPrefersSubtitles): Ditto.
+        (WebCore::trackDisplayName): Update logic used to build track title.
+        (WebCore::CaptionUserPreferencesMac::displayNameForTrack): Call trackDisplayName.
+        (WebCore::textTrackCompare): Text track title sort comparison function.
+        (WebCore::CaptionUserPreferencesMac::sortedTrackListForMenu): New. Sort the list of tracks
+            according to title, language, and user preferences.
+
+        * platform/Language.cpp:
+        (WebCore::displayNameForLanguageLocale): Don't leak the CFLocale.
+        
+        * platform/LocalizedStrings.cpp:
+        (WebCore::textTrackCountryAndLanguageMenuItemText): New.
+        (WebCore::textTrackLanguageMenuItemText): Ditto.
+        (WebCore::closedCaptionTrackMenuItemText): Ditto.
+        (WebCore::sdhTrackMenuItemText): Ditto.
+        (WebCore::easyReaderTrackMenuItemText): Ditto.
+        * platform/LocalizedStrings.h:
+
+        * platform/graphics/InbandTextTrackPrivate.h:
+        * platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.mm:
+        (WebCore::InbandTextTrackPrivateAVFObjC::containsOnlyForcedSubtitles): New, return AVFoundation property.
+        (WebCore::InbandTextTrackPrivateAVFObjC::isMainProgramContent): Ditto.
+        (WebCore::InbandTextTrackPrivateAVFObjC::isEasyToRead): Ditto.
+
+        * testing/InternalSettings.cpp:
+        (WebCore::InternalSettings::setShouldDisplayTrackKind): Set setting via CaptionUserPreferences
+            instead of from Settings.
+        (WebCore::InternalSettings::shouldDisplayTrackKind): Get setting from CaptionUserPreferences
+            instead of from Settings.
+
 2013-03-20  Zan Dobersek  <zdobersek@igalia.com>
 
         [GTK] Clean up TemporaryLinkStubs.cpp
index 87fabef..e57d002 100644 (file)
Binary files a/Source/WebCore/English.lproj/Localizable.strings and b/Source/WebCore/English.lproj/Localizable.strings differ
index ae2d3b5..5e230d8 100644 (file)
@@ -320,7 +320,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
     if (document->page()) {
         CaptionUserPreferences* captionPreferences = document->page()->group().captionPreferences();
         if (captionPreferences->userHasCaptionPreferences())
-            m_disableCaptions = !captionPreferences->userPrefersCaptions();
+            m_disableCaptions = !captionPreferences->shouldShowCaptions();
     }
 #endif
 }
@@ -2862,7 +2862,7 @@ void HTMLMediaElement::setSelectedTextTrack(PassRefPtr<PlatformTextTrack> platfo
     TrackDisplayUpdateScope scope(this);
 
     if (!platformTrack) {
-        toggleTrackAtIndex(textTracksOffIndex(), true);
+        setSelectedTextTrack(0);
         return;
     }
 
@@ -2877,7 +2877,7 @@ void HTMLMediaElement::setSelectedTextTrack(PassRefPtr<PlatformTextTrack> platfo
 
     if (i == m_textTracks->length())
         return;
-    toggleTrackAtIndex(i, true);
+    setSelectedTextTrack(textTrack);
 }
 
 Vector<RefPtr<PlatformTextTrack> > HTMLMediaElement::platformTextTracks()
@@ -3076,7 +3076,7 @@ bool HTMLMediaElement::userPrefersCaptions() const
         return false;
 
     CaptionUserPreferences* captionPreferences = page->group().captionPreferences();
-    return captionPreferences->userHasCaptionPreferences() && captionPreferences->userPrefersCaptions();
+    return captionPreferences->userHasCaptionPreferences() && captionPreferences->shouldShowCaptions();
 }
 
 bool HTMLMediaElement::userIsInterestedInThisTrackKind(String kind) const
@@ -3084,15 +3084,19 @@ bool HTMLMediaElement::userIsInterestedInThisTrackKind(String kind) const
     if (m_disableCaptions)
         return false;
 
-    Settings* settings = document()->settings();
+    Page* page = document()->page();
+    if (!page)
+        return false;
+
+    CaptionUserPreferences* captionPreferences = page->group().captionPreferences();
     bool userPrefersCaptionsOrSubtitles = m_closedCaptionsVisible || userPrefersCaptions();
 
     if (kind == TextTrack::subtitlesKeyword())
-        return (settings && settings->shouldDisplaySubtitles()) || userPrefersCaptionsOrSubtitles;
+        return captionPreferences->userPrefersSubtitles() || userPrefersCaptionsOrSubtitles;
     if (kind == TextTrack::captionsKeyword())
-        return (settings && settings->shouldDisplayCaptions()) || userPrefersCaptionsOrSubtitles;
+        return captionPreferences->userPrefersCaptions() || userPrefersCaptionsOrSubtitles;
     if (kind == TextTrack::descriptionsKeyword())
-        return settings && settings->shouldDisplayTextDescriptions();
+        return captionPreferences->userPrefersTextDescriptions() || userPrefersCaptionsOrSubtitles;
 
     return false;
 }
@@ -3178,25 +3182,27 @@ void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
         trackToEnable->setMode(TextTrack::showingKeyword());
 }
 
-void HTMLMediaElement::toggleTrackAtIndex(int index, bool exclusive)
+void HTMLMediaElement::setSelectedTextTrack(TextTrack* trackToSelect)
 {
     TextTrackList* trackList = textTracks();
     if (!trackList || !trackList->length())
         return;
-
-    CaptionUserPreferences* captionPreferences = document()->page() ? document()->page()->group().captionPreferences() : 0;
-    if (captionPreferences)
-        captionPreferences->setUserPrefersCaptions(index != textTracksOffIndex());
+    if (trackToSelect && !trackList->contains(trackToSelect))
+        return;
 
     for (int i = 0, length = trackList->length(); i < length; ++i) {
         TextTrack* track = trackList->item(i);
-        if (i == index) {
-            track->setMode(TextTrack::showingKeyword());
-            if (captionPreferences && track->language().length())
-                captionPreferences->setPreferredLanguage(track->language());
-        }
-        else if (exclusive || index == HTMLMediaElement::textTracksOffIndex())
+        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())
+            captionPreferences->setPreferredLanguage(trackToSelect->language());
     }
 }
 
@@ -4520,11 +4526,15 @@ void HTMLMediaElement::captionPreferencesChanged()
     if (!isVideo())
         return;
 
-    m_processingPreferenceChange = true;
-    setClosedCaptionsVisible(userPrefersCaptions());
-
     if (hasMediaControls())
         mediaControls()->textTrackPreferencesChanged();
+
+    bool prefersCaptions = userPrefersCaptions();
+    if (m_disableCaptions == !prefersCaptions)
+        return;
+
+    m_processingPreferenceChange = true;
+    setClosedCaptionsVisible(prefersCaptions);
 }
 
 void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured()
index 08c2dc6..3e00ed7 100644 (file)
@@ -267,7 +267,7 @@ public:
     void configureTextTracks();
     void configureTextTrackGroup(const TrackGroup&);
 
-    void toggleTrackAtIndex(int index, bool exclusive = true);
+    void setSelectedTextTrack(TextTrack*);
     static int textTracksOffIndex() { return -1; }
     static int textTracksIndexNotFound() { return -2; }
 
index 10392ec..e14a647 100644 (file)
@@ -755,6 +755,13 @@ void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* even
         // Check if the event target has such a custom element and, if so,
         // tell the HTMLMediaElement to enable that track.
 
+        RefPtr<TextTrack> textTrack;
+        MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(toElement(target));
+        if (iter != m_menuToTrackMap.end())
+            textTrack = iter->value;
+        m_menuToTrackMap.clear();
+        m_controls->toggleClosedCaptionTrackList();
+
         int trackIndex = trackListIndexForElement(toElement(target));
         if (trackIndex == HTMLMediaElement::textTracksIndexNotFound())
             return;
@@ -763,10 +770,11 @@ void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* even
         if (!mediaElement)
             return;
 
-        mediaElement->toggleTrackAtIndex(trackIndex);
+        if (textTrack)
+            mediaElement->setSelectedTextTrack(textTrack.get());
+        else if (trackIndex == HTMLMediaElement::textTracksOffIndex())
+            mediaElement->setSelectedTextTrack(0);
 
-        // We've selected a track to display, so we can now close the menu.
-        m_controls->toggleClosedCaptionTrackList();
         updateDisplay();
     }
 
@@ -799,53 +807,37 @@ void MediaControlClosedCaptionsTrackListElement::updateDisplay()
 
     if (m_trackListHasChanged)
         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()) {
-            if (trackIndex == HTMLMediaElement::textTracksOffIndex()) {
-                if (captionsVisible)
-                    trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
-                else
-                    trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
-            } else {
-                TextTrack* track = trackList->item(trackIndex);
-                if (!track)
-                    continue;
-                if (track->mode() == TextTrack::showingKeyword())
-                    trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
-                else
-                    trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
-            }
-        }
-    }
-#endif
-}
+        if (trackIndex == HTMLMediaElement::textTracksIndexNotFound())
+            continue;
 
-#if ENABLE(VIDEO_TRACK)
-static void insertTextTrackMenuItemIntoSortedContainer(RefPtr<Element>& item, RefPtr<Element>& container)
-{
-    // The container will always have the "Off" entry already present and it
-    // should remain at the start of the list.
-    ASSERT(container->childNodeCount() > 0);
-    ASSERT(item->childNodeCount() == 1); // Each item should have a single text node child for the label.
-    String itemLabel = toText(item->firstChild())->wholeText();
-
-    // This is an insertion sort :( However, there shouldn't be a horrible number of text track items.
-    for (int i = 1, numChildNodes = container->childNodeCount(); i < numChildNodes; ++i) {
-        Node* child = container->childNode(i);
-        ASSERT(child->childNodeCount() == 1); // Each item should have a single text node child for the label.
-        String childLabel = toText(child->firstChild())->wholeText();
-        if (codePointCompareLessThan(itemLabel, childLabel)) {
-            container->insertBefore(item, child);
-            return;
+        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())
+            continue;
+        textTrack = iter->value;
+        if (!textTrack)
+            continue;
+        if (textTrack->mode() == TextTrack::showingKeyword())
+            trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
+        else
+            trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
     }
-    container->appendChild(item);
-}
 #endif
+}
 
 void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
 {
@@ -855,6 +847,7 @@ void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
     m_menuItems.clear();
 
     m_trackListHasChanged = false;
+    m_menuToTrackMap.clear();
 
     if (!mediaController()->hasClosedCaptions())
         return;
@@ -864,43 +857,40 @@ void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
         return;
 
     TextTrackList* trackList = mediaElement->textTracks();
-
     if (!trackList || !trackList->length())
         return;
 
     Document* doc = document();
     CaptionUserPreferences* captionsUserPreferences = doc->page()->group().captionPreferences();
+    Vector<RefPtr<TextTrack> > tracksForMenu = captionsUserPreferences->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> trackItem;
-
-    trackItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
-    trackItem->appendChild(doc->createTextNode(textTrackOffText()));
-    trackItem->setAttribute(trackIndexAttributeName(), textTracksOffAttrValue, ASSERT_NO_EXCEPTION);
-    captionsMenuList->appendChild(trackItem);
-    m_menuItems.append(trackItem);
+    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 = trackList->length(); i < length; ++i) {
-        TextTrack* track = trackList->item(i);
-        trackItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+    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.
-        trackItem->setAttribute(trackIndexAttributeName(), String::number(i), ASSERT_NO_EXCEPTION);
+        menuItem->setAttribute(trackIndexAttributeName(), String::number(i), ASSERT_NO_EXCEPTION);
 
-        if (captionsUserPreferences)
-            trackItem->appendChild(doc->createTextNode(captionsUserPreferences->displayNameForTrack(track)));
-        else
-            trackItem->appendChild(doc->createTextNode(track->label()));
+        menuItem->appendChild(doc->createTextNode(captionsUserPreferences->displayNameForTrack(textTrack.get())));
 
-        insertTextTrackMenuItemIntoSortedContainer(trackItem, captionsMenuList);
-        m_menuItems.append(trackItem);
+        captionsMenuList->appendChild(menuItem);
+        m_menuItems.append(menuItem);
+        m_menuToTrackMap.add(menuItem, textTrack);
     }
 
     appendChild(captionsMenuList);
index 0051e7a..cf96d24 100644 (file)
@@ -315,6 +315,10 @@ private:
 
     typedef Vector<RefPtr<Element> > TrackMenuItems;
     TrackMenuItems m_menuItems;
+#if ENABLE(VIDEO_TRACK)
+    typedef HashMap<RefPtr<Element>, RefPtr<TextTrack> > MenuItemToTrackMap;
+    MenuItemToTrackMap m_menuToTrackMap;
+#endif
     MediaControls* m_controls;
     bool m_trackListHasChanged;
 };
index 5c6fdd8..36e1780 100644 (file)
@@ -411,6 +411,7 @@ void MediaControls::textTrackPreferencesChanged()
 {
     if (m_textDisplayContainer)
         m_textDisplayContainer->updateSizes(true);
+    closedCaptionTracksChanged();
 }
 #endif
 
index c569111..d252f53 100644 (file)
@@ -103,6 +103,30 @@ bool InbandTextTrack::isClosedCaptions() const
     return m_private->isClosedCaptions();
 }
 
+bool InbandTextTrack::containsOnlyForcedSubtitles() const
+{
+    if (!m_private)
+        return false;
+    
+    return m_private->containsOnlyForcedSubtitles();
+}
+
+bool InbandTextTrack::isMainProgramContent() const
+{
+    if (!m_private)
+        return false;
+    
+    return m_private->isMainProgramContent();
+}
+
+bool InbandTextTrack::isEasyToRead() const
+{
+    if (!m_private)
+        return false;
+    
+    return m_private->isEasyToRead();
+}
+    
 size_t InbandTextTrack::inbandTrackIndex()
 {
     ASSERT(m_private);
index f338bea..3a9f0c2 100644 (file)
@@ -46,6 +46,9 @@ public:
     virtual ~InbandTextTrack();
 
     virtual bool isClosedCaptions() const OVERRIDE;
+    virtual bool containsOnlyForcedSubtitles() const OVERRIDE;
+    virtual bool isMainProgramContent() const OVERRIDE;
+    virtual bool isEasyToRead() const OVERRIDE;
     virtual void setMode(const AtomicString&) OVERRIDE;
     size_t inbandTrackIndex();
 
index 155410d..bb5698c 100644 (file)
@@ -429,6 +429,15 @@ PassRefPtr<PlatformTextTrack> TextTrack::platformTextTrack()
 }
 #endif
 
+bool TextTrack::isMainProgramContent() const
+{
+    // "Main program" content is intrinsic to the presentation of the media file, regardless of locale. Content such as
+    // directors commentary is not "main program" because it is not essential for the presentation. HTML5 doesn't have
+    // a way to express this in a machine-reable form, it is typically done with the track label, so we assume that caption
+    // tracks are main content and all other track types are not.
+    return m_kind == captionsKeyword();
+}
+
 } // namespace WebCore
 
 #endif
index c82a979..92904e0 100644 (file)
@@ -119,6 +119,10 @@ public:
 
     virtual bool isClosedCaptions() const { return false; }
 
+    virtual bool containsOnlyForcedSubtitles() const { return false; }
+    virtual bool isMainProgramContent() const;
+    virtual bool isEasyToRead() const { return false; }
+
     int trackIndex();
     void invalidateTrackIndex();
 
index 4223c06..293b3d0 100644 (file)
@@ -211,6 +211,22 @@ void TextTrackList::remove(TextTrack* track)
     tracks->remove(index);
 }
 
+bool TextTrackList::contains(TextTrack* track) const
+{
+    const Vector<RefPtr<TextTrack> >* tracks = 0;
+    
+    if (track->trackType() == TextTrack::TrackElement)
+        tracks = &m_elementTracks;
+    else if (track->trackType() == TextTrack::AddTrack)
+        tracks = &m_addTrackTracks;
+    else if (track->trackType() == TextTrack::InBand)
+        tracks = &m_inbandTracks;
+    else
+        ASSERT_NOT_REACHED();
+    
+    return tracks->find(track) != notFound;
+}
+
 const AtomicString& TextTrackList::interfaceName() const
 {
     return eventNames().interfaceForTextTrackList;
index 3c1de9d..1ee15c9 100644 (file)
@@ -53,6 +53,7 @@ public:
     unsigned length() const;
     int getTrackIndex(TextTrack*);
     int getTrackIndexRelativeToRenderedTracks(TextTrack*);
+    bool contains(TextTrack*) const;
 
     TextTrack* item(unsigned index);
     void append(PassRefPtr<TextTrack>);
index 765854f..43282ea 100644 (file)
 #if ENABLE(VIDEO_TRACK)
 
 #include "CaptionUserPreferences.h"
+#include "Page.h"
 #include "PageGroup.h"
+#include "Settings.h"
+#include "TextTrackList.h"
+#include <wtf/NonCopyingSort.h>
 
 namespace WebCore {
 
+CaptionUserPreferences::CaptionUserPreferences(PageGroup* group)
+    : m_pageGroup(group)
+    , m_timer(this, &CaptionUserPreferences::timerFired)
+    , m_testingMode(false)
+    , m_havePreferences(false)
+    , m_shouldShowCaptions(false)
+{
+}
+
+CaptionUserPreferences::~CaptionUserPreferences()
+{
+}
+
+bool CaptionUserPreferences::shouldShowCaptions() const
+{
+    return m_testingMode ? m_shouldShowCaptions : false;
+}
+
+void CaptionUserPreferences::timerFired(Timer<CaptionUserPreferences>*)
+{
+    captionPreferencesChanged();
+}
+
+void CaptionUserPreferences::notify()
+{
+    if (!m_testingMode)
+        return;
+
+    m_havePreferences = true;
+    if (!m_timer.isActive())
+        m_timer.startOneShot(0);
+}
+
+void CaptionUserPreferences::setShouldShowCaptions(bool preference)
+{
+    m_shouldShowCaptions = preference;
+    notify();
+}
+
+bool CaptionUserPreferences::userPrefersCaptions() const
+{
+    Page* page = *(pageGroup()->pages().begin());
+    if (!page)
+        return false;
+
+    return page->settings()->shouldDisplayCaptions();
+}
+
 void CaptionUserPreferences::setUserPrefersCaptions(bool preference)
 {
-    m_userPrefersCaptions = preference;
-    if (m_testingMode) {
-        m_havePreferences = true;
-        captionPreferencesChanged();
-    }
+    Page* page = *(pageGroup()->pages().begin());
+    if (!page)
+        return;
+
+    page->settings()->setShouldDisplayCaptions(preference);
+    notify();
+}
+
+bool CaptionUserPreferences::userPrefersSubtitles() const
+{
+    Page* page = *(pageGroup()->pages().begin());
+    if (!page)
+        return false;
+
+    return page->settings()->shouldDisplaySubtitles();
+}
+
+void CaptionUserPreferences::setUserPrefersSubtitles(bool preference)
+{
+    Page* page = *(pageGroup()->pages().begin());
+    if (!page)
+        return;
+
+    page->settings()->setShouldDisplaySubtitles(preference);
+    notify();
+}
+
+bool CaptionUserPreferences::userPrefersTextDescriptions() const
+{
+    Page* page = *(pageGroup()->pages().begin());
+    if (!page)
+        return false;
+    
+    return page->settings()->shouldDisplayTextDescriptions();
+}
+
+void CaptionUserPreferences::setUserPrefersTextDescriptions(bool preference)
+{
+    Page* page = *(pageGroup()->pages().begin());
+    if (!page)
+        return;
+    
+    page->settings()->setShouldDisplayTextDescriptions(preference);
+    notify();
 }
 
 void CaptionUserPreferences::captionPreferencesChanged()
@@ -58,13 +149,10 @@ Vector<String> CaptionUserPreferences::preferredLanguages() const
 void CaptionUserPreferences::setPreferredLanguage(String language)
 {
     m_userPreferredLanguage = language;
-    if (m_testingMode) {
-        m_havePreferences = true;
-        captionPreferencesChanged();
-    }
+    notify();
 }
 
-String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const
+static String trackDisplayName(TextTrack* track)
 {
     if (track->label().isEmpty() && track->language().isEmpty())
         return textTrackNoLabelText();
@@ -73,6 +161,30 @@ String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const
     return track->language();
 }
 
+String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const
+{
+    return trackDisplayName(track);
+}
+    
+static bool textTrackCompare(const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b)
+{
+    return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
+}
+
+Vector<RefPtr<TextTrack> > CaptionUserPreferences::sortedTrackListForMenu(TextTrackList* trackList)
+{
+    ASSERT(trackList);
+
+    Vector<RefPtr<TextTrack> > tracksForMenu;
+
+    for (unsigned i = 0, length = trackList->length(); i < length; ++i)
+        tracksForMenu.append(trackList->item(i));
+
+    nonCopyingSort(tracksForMenu.begin(), tracksForMenu.end(), textTrackCompare);
+
+    return tracksForMenu;
+}
+
 }
 
 #endif // ENABLE(VIDEO_TRACK)
index c0577c8..e4d4e6a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013  Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "Language.h"
 #include "LocalizedStrings.h"
 #include "TextTrack.h"
+#include "Timer.h"
 #include <wtf/PassOwnPtr.h>
 #include <wtf/text/AtomicString.h>
 
 namespace WebCore {
 
 class PageGroup;
+class TextTrackList;
 
 class CaptionUserPreferences {
 public:
     static PassOwnPtr<CaptionUserPreferences> create(PageGroup* group) { return adoptPtr(new CaptionUserPreferences(group)); }
-    virtual ~CaptionUserPreferences() { }
+    virtual ~CaptionUserPreferences();
 
     virtual bool userHasCaptionPreferences() const { return m_testingMode && m_havePreferences; }
-    virtual bool userPrefersCaptions() const { return m_testingMode ? m_userPrefersCaptions : false; }
-    virtual void setUserPrefersCaptions(bool preference);
+    virtual bool shouldShowCaptions() const;
+    virtual void setShouldShowCaptions(bool);
+
+    virtual bool userPrefersCaptions() const;
+    virtual void setUserPrefersCaptions(bool);
+
+    virtual bool userPrefersSubtitles() const;
+    virtual void setUserPrefersSubtitles(bool preference);
+    
+    virtual bool userPrefersTextDescriptions() const;
+    virtual void setUserPrefersTextDescriptions(bool preference);
+
     virtual float captionFontSizeScale(bool& important) const { important = false; return 0.05f; }
     virtual String captionsStyleSheetOverride() const { return emptyString(); }
 
@@ -57,27 +69,26 @@ public:
     virtual Vector<String> preferredLanguages() const;
 
     virtual String displayNameForTrack(TextTrack*) const;
+    virtual Vector<RefPtr<TextTrack> > sortedTrackListForMenu(TextTrackList*);
 
     virtual bool testingMode() const { return m_testingMode; }
     virtual void setTestingMode(bool override) { m_testingMode = override; }
 
-    PageGroup* pageGroup() { return m_pageGroup; }
+    PageGroup* pageGroup() const { return m_pageGroup; }
 
 protected:
-    CaptionUserPreferences(PageGroup* group)
-        : m_pageGroup(group)
-        , m_testingMode(false)
-        , m_havePreferences(false)
-        , m_userPrefersCaptions(false)
-    {
-    }
+    CaptionUserPreferences(PageGroup*);
 
 private:
+    void timerFired(Timer<CaptionUserPreferences>*);
+    void notify();
+
     PageGroup* m_pageGroup;
+    Timer<CaptionUserPreferences> m_timer;
     String m_userPreferredLanguage;
     bool m_testingMode;
     bool m_havePreferences;
-    bool m_userPrefersCaptions;
+    bool m_shouldShowCaptions;
 };
     
 }
index 52b533a..88196c7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -42,8 +42,12 @@ public:
 
 #if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
     virtual bool userHasCaptionPreferences() const OVERRIDE;
+    virtual bool shouldShowCaptions() const OVERRIDE;
+    virtual void setShouldShowCaptions(bool) OVERRIDE;
+
     virtual bool userPrefersCaptions() const OVERRIDE;
-    virtual void setUserPrefersCaptions(bool) OVERRIDE;
+    virtual bool userPrefersSubtitles() const OVERRIDE;
+    
     virtual float captionFontSizeScale(bool&) const OVERRIDE;
     virtual String captionsStyleSheetOverride() const OVERRIDE;
 
@@ -53,8 +57,10 @@ public:
     virtual Vector<String> preferredLanguages() const OVERRIDE;
 
     virtual void captionPreferencesChanged() OVERRIDE;
+
 #endif
 
+    virtual Vector<RefPtr<TextTrack> > sortedTrackListForMenu(TextTrackList*) OVERRIDE;
     virtual String displayNameForTrack(TextTrack*) const OVERRIDE;
 
 private:
index ed32b7b..0f67ca3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,7 +41,9 @@
 #import "PageGroup.h"
 #import "SoftLinking.h"
 #import "TextTrackCue.h"
+#import "TextTrackList.h"
 #import "UserStyleSheetTypes.h"
+#import <wtf/NonCopyingSort.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/text/StringBuilder.h>
 
@@ -71,6 +73,7 @@ SOFT_LINK(MediaAccessibility, MACaptionAppearanceGetRelativeCharacterSize, CGFlo
 SOFT_LINK(MediaAccessibility, MACaptionAppearanceGetTextEdgeStyle, MACaptionAppearanceTextEdgeStyle, (MACaptionAppearanceDomain domain, MACaptionAppearanceBehavior *behavior), (domain, behavior))
 SOFT_LINK(MediaAccessibility, MACaptionAppearanceAddSelectedLanguage, bool, (MACaptionAppearanceDomain domain, CFStringRef language), (domain, language));
 SOFT_LINK(MediaAccessibility, MACaptionAppearanceCopySelectedLanguages, CFArrayRef, (MACaptionAppearanceDomain domain), (domain));
+SOFT_LINK(MediaAccessibility, MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics,  CFArrayRef, (MACaptionAppearanceDomain domain), (domain));
 
 SOFT_LINK_POINTER(MediaAccessibility, kMAXCaptionAppearanceSettingsChangedNotification, CFStringRef)
 #define kMAXCaptionAppearanceSettingsChangedNotification getkMAXCaptionAppearanceSettingsChangedNotification()
@@ -111,24 +114,45 @@ CaptionUserPreferencesMac::~CaptionUserPreferencesMac()
 }
 
 #if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
-bool CaptionUserPreferencesMac::userPrefersCaptions() const
+
+bool CaptionUserPreferencesMac::shouldShowCaptions() const
 {
     if (testingMode() || !MediaAccessibilityLibrary())
-        return CaptionUserPreferences::userPrefersCaptions();
+        return CaptionUserPreferences::shouldShowCaptions();
 
     return MACaptionAppearanceGetShowCaptions(kMACaptionAppearanceDomainUser);
 }
 
-void CaptionUserPreferencesMac::setUserPrefersCaptions(bool preference)
+void CaptionUserPreferencesMac::setShouldShowCaptions(bool preference)
 {
     if (testingMode() || !MediaAccessibilityLibrary()) {
-        CaptionUserPreferences::setUserPrefersCaptions(preference);
+        CaptionUserPreferences::setShouldShowCaptions(preference);
         return;
     }
 
     MACaptionAppearanceSetShowCaptions(kMACaptionAppearanceDomainUser, preference);
 }
 
+bool CaptionUserPreferencesMac::userPrefersCaptions() const
+{
+    bool captionSetting = CaptionUserPreferences::userPrefersCaptions();
+    if (captionSetting || testingMode() || !MediaAccessibilityLibrary())
+        return captionSetting;
+    
+    RetainPtr<CFArrayRef> captioningMediaCharacteristics(AdoptCF, MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser));
+    return captioningMediaCharacteristics && CFArrayGetCount(captioningMediaCharacteristics.get());
+}
+
+bool CaptionUserPreferencesMac::userPrefersSubtitles() const
+{
+    bool subtitlesSetting = CaptionUserPreferences::userPrefersSubtitles();
+    if (subtitlesSetting || testingMode() || !MediaAccessibilityLibrary())
+        return subtitlesSetting;
+    
+    RetainPtr<CFArrayRef> captioningMediaCharacteristics(AdoptCF, MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser));
+    return !(captioningMediaCharacteristics && CFArrayGetCount(captioningMediaCharacteristics.get()));
+}
+
 bool CaptionUserPreferencesMac::userHasCaptionPreferences() const
 {
     if (testingMode() || !MediaAccessibilityLibrary())
@@ -499,40 +523,186 @@ Vector<String> CaptionUserPreferencesMac::preferredLanguages() const
 
     return userPreferredLanguages;
 }
-#endif
-
-String CaptionUserPreferencesMac::displayNameForTrack(TextTrack* track) const
+#endif  // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK)
+    
+static String trackDisplayName(TextTrack* track)
 {
-    String label = track->label();
-    String language = track->language();
-    String preferredLanguage = defaultLanguage();
     StringBuilder displayName;
-
-    if (label.isEmpty() && language.isEmpty()) {
+    String label = track->label();
+    String trackLanguageIdentifier = track->language();
+
+    RetainPtr<CFLocaleRef> currentLocale(AdoptCF, CFLocaleCopyCurrent());
+    RetainPtr<CFStringRef> localeIdentifier(AdoptCF, CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, trackLanguageIdentifier.createCFString().get()));
+    RetainPtr<CFStringRef> languageCF(AdoptCF, CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleLanguageCode, localeIdentifier.get()));
+    String language = languageCF.get();
+    if (!label.isEmpty()) {
+        if (!language.isEmpty() && !label.contains(language)) {
+            RetainPtr<CFDictionaryRef> localeDict(AdoptCF, CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorDefault, localeIdentifier.get()));
+            if (localeDict) {
+                CFStringRef countryCode = 0;
+                String countryName;
+                
+                CFDictionaryGetValueIfPresent(localeDict.get(), kCFLocaleCountryCode, (const void **)&countryCode);
+                if (countryCode) {
+                    RetainPtr<CFStringRef> countryNameCF(AdoptCF, CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleCountryCode, countryCode));
+                    countryName = countryNameCF.get();
+                }
+                
+                if (!countryName.isEmpty())
+                    displayName.append(textTrackCountryAndLanguageMenuItemText(label, countryName, language));
+                else
+                    displayName.append(textTrackLanguageMenuItemText(label, language));
+            }
+        }
+    } else {
+        String languageAndLocale = CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleIdentifier, trackLanguageIdentifier.createCFString().get());
+        if (!languageAndLocale.isEmpty())
+            displayName.append(languageAndLocale);
+        else if (!language.isEmpty())
+            displayName.append(language);
+        else
+            displayName.append(localeIdentifier.get());
+    }
+    
+    if (displayName.isEmpty())
         displayName.append(textTrackNoLabelText());
+    
+    if (track->isEasyToRead())
+        return easyReaderTrackMenuItemText(displayName.toString());
+    
+    if (track->kind() != track->captionsKeyword())
         return displayName.toString();
+    
+    if (track->isClosedCaptions())
+        return closedCaptionTrackMenuItemText(displayName.toString());
+    
+    return sdhTrackMenuItemText(displayName.toString());
+}
+
+String CaptionUserPreferencesMac::displayNameForTrack(TextTrack* track) const
+{
+    return trackDisplayName(track);
+}
+
+static String languageIdentifier(const String& languageCode)
+{
+    if (languageCode.isEmpty())
+        return languageCode;
+
+    String lowercaseLanguageCode = languageCode.lower();
+    
+    if (lowercaseLanguageCode.length() >= 3 && (lowercaseLanguageCode[2] == '_' || lowercaseLanguageCode[2] == '-'))
+        lowercaseLanguageCode.truncate(2);
+    
+    return lowercaseLanguageCode;
+}
+
+static bool textTrackCompare(const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b)
+{
+    String preferredLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(defaultLanguage()));
+    String aLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(a->language()));
+    String bLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(b->language()));
+
+    // Tracks in the user's preferred language are always at the top of the menu.
+    bool aIsPreferredLanguage = !codePointCompare(aLanguageDisplayName, preferredLanguageDisplayName);
+    bool bIsPreferredLanguage = !codePointCompare(bLanguageDisplayName, preferredLanguageDisplayName);
+    if ((aIsPreferredLanguage || bIsPreferredLanguage) && (aIsPreferredLanguage != bIsPreferredLanguage))
+        return aIsPreferredLanguage;
+
+    // Tracks not in the user's preferred language sort first by language ...
+    if (codePointCompare(aLanguageDisplayName, bLanguageDisplayName))
+        return codePointCompare(aLanguageDisplayName, bLanguageDisplayName) < 0;
+
+    // ... but when tracks have the same language, main program content sorts next highest ...
+    bool aIsMainContent = a->isMainProgramContent();
+    bool bIsMainContent = b->isMainProgramContent();
+    if ((aIsMainContent || bIsMainContent) && (aIsMainContent != bIsMainContent))
+        return aIsMainContent;
+
+    // ... and main program trakcs sort higher than CC tracks ...
+    bool aIsCC = a->isClosedCaptions();
+    bool bIsCC = b->isClosedCaptions();
+    if ((aIsCC || bIsCC) && (aIsCC != bIsCC)) {
+        if (aIsCC)
+            return aIsMainContent;
+        return bIsMainContent;
     }
 
-    if (!label.isEmpty())
-        displayName.append(label);
+    // ... and tracks of the same type and language sort by the menu item text.
+    return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
+}
+
+Vector<RefPtr<TextTrack> > CaptionUserPreferencesMac::sortedTrackListForMenu(TextTrackList* trackList)
+{
+    ASSERT(trackList);
+
+    Vector<RefPtr<TextTrack> > tracksForMenu;
+    HashSet<String> languagesIncluded;
+    bool prefersAccessibilityTracks = userPrefersCaptions();
+
+    for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
+        TextTrack* track = trackList->item(i);
+        String language = displayNameForLanguageLocale(track->language());
 
-    AtomicString localeDisplayName = displayNameForLanguageLocale(language);
-    if (!label.contains(localeDisplayName)) {
-        if (displayName.length() > 0)
-            displayName.append(" ");
-        displayName.append(localeDisplayName);
+        if (track->isEasyToRead()) {
+            if (!language.isEmpty())
+                languagesIncluded.add(language);
+            tracksForMenu.append(track);
+            continue;
+        }
+
+        if (track->containsOnlyForcedSubtitles())
+            continue;
+
+        if (!language.isEmpty() && track->isMainProgramContent()) {
+            bool isAccessibilityTrack = track->kind() == track->captionsKeyword();
+            if (prefersAccessibilityTracks) {
+                // In the first pass, include only caption tracks if the user prefers accessibility tracks.
+                if (!isAccessibilityTrack) {
+                    LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - skipping %s because it is NOT an accessibility track", language.utf8().data());
+                    continue;
+                }
+            } else {
+                // In the first pass, only include the first non-CC or SDH track with each language if the user prefers translation tracks.
+                if (isAccessibilityTrack) {
+                    LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - skipping %s because it is an accessibility track", language.utf8().data());
+                    continue;
+                }
+                if (languagesIncluded.contains(language)) {
+                    LOG(Media, "CaptionUserPreferencesMac::sortedTrackListForMenu - skipping %s because it is not the first with this language", language.utf8().data());
+                    continue;
+                }
+            }
+        }
+
+        if (!language.isEmpty())
+            languagesIncluded.add(language);
+        tracksForMenu.append(track);
     }
 
-    if (track->kind() == track->captionsKeyword()) {
-        if (track->isClosedCaptions())
-            displayName.append(" CC");
-        else
-            displayName.append(" SDH");
+    // Now that we have filtered for the user's accessibility/translation preference, add  all tracks with a unique language without regard to track type.
+    for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
+        TextTrack* track = trackList->item(i);
+        String language = displayNameForLanguageLocale(track->language());
+
+        // All candidates with no languge were added the first time through.
+        if (language.isEmpty())
+            continue;
+
+        if (track->containsOnlyForcedSubtitles())
+            continue;
+        if (!languagesIncluded.contains(language) && track->isMainProgramContent()) {
+            languagesIncluded.add(language);
+            tracksForMenu.append(track);
+        }
     }
 
-    return displayName.toString();
-}
+    nonCopyingSort(tracksForMenu.begin(), tracksForMenu.end(), textTrackCompare);
 
+    return tracksForMenu;
+}
+    
 }
 
 #endif // ENABLE(VIDEO_TRACK)
index ff102b9..8bc8f48 100644 (file)
@@ -154,8 +154,10 @@ String preferredLanguageFromList(const Vector<String>& languageList, const Vecto
 String displayNameForLanguageLocale(const String& localeName)
 {
 #if PLATFORM(MAC)
-    if (!localeName.isNull() && !localeName.isEmpty())
-        return CFLocaleCopyDisplayNameForPropertyValue(CFLocaleCopyCurrent(), kCFLocaleIdentifier, localeName.createCFString().get());
+    if (!localeName.isNull() && !localeName.isEmpty()) {
+        RetainPtr<CFLocaleRef> currentLocale(AdoptCF, CFLocaleCopyCurrent());
+        return CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleIdentifier, localeName.createCFString().get());
+    }
 #endif
     return localeName;
 }
index 3822a14..0de7e93 100644 (file)
@@ -1027,8 +1027,36 @@ String textTrackOffText()
 
 String textTrackNoLabelText()
 {
-    return WEB_UI_STRING_KEY("Unknown", "Unknown (closed captions track)", "Menu item label for a closed captions track that has no other name");
+    return WEB_UI_STRING_KEY("Unknown", "Unknown (text track)", "Menu item label for a text track that has no other name");
 }
+    
+#if PLATFORM(MAC)
+String textTrackCountryAndLanguageMenuItemText(const String& title, const String& country, const String& language)
+{
+    return formatLocalizedString(WEB_UI_STRING("%@ (%@-%@)", "Text track display name format that includes the country and language of the subtitle, in the form of 'Title (Language-Country)'"), title.createCFString().get(), language.createCFString().get(), country.createCFString().get());
+}
+
+String textTrackLanguageMenuItemText(const String& title, const String& language)
+{
+    return formatLocalizedString(WEB_UI_STRING("%@ (%@)", "Text track display name format that includes the language of the subtitle, in the form of 'Title (Language)'"), title.createCFString().get(), language.createCFString().get());
+}
+
+String closedCaptionTrackMenuItemText(const String& title)
+{
+    return formatLocalizedString(WEB_UI_STRING("%@ CC", "Text track contains closed captions"), title.createCFString().get());
+}
+
+String sdhTrackMenuItemText(const String& title)
+{
+    return formatLocalizedString(WEB_UI_STRING("%@ SDH", "Text track contains subtitles for the deaf and hard of hearing"), title.createCFString().get());
+}
+
+String easyReaderTrackMenuItemText(const String& title)
+{
+    return formatLocalizedString(WEB_UI_STRING("%@ Easy Reader", "Text track contains simplified (3rd grade level) subtitles"), title.createCFString().get());
+}
+#endif
+
 #endif
 
 String snapshottedPlugInLabelTitle()
index fd81ff8..4dbc6ee 100644 (file)
@@ -268,6 +268,13 @@ namespace WebCore {
     String textTrackSubtitlesText();
     String textTrackOffText();
     String textTrackNoLabelText();
+#if PLATFORM(MAC)
+    String textTrackCountryAndLanguageMenuItemText(const String& title, const String& country, const String& language);
+    String textTrackLanguageMenuItemText(const String& title, const String& language);
+    String closedCaptionTrackMenuItemText(const String&);
+    String sdhTrackMenuItemText(const String&);
+    String easyReaderTrackMenuItemText(const String&);
+#endif
 #endif
 
     String snapshottedPlugInLabelTitle();
index d2b94af..b3f7b2e 100644 (file)
@@ -56,6 +56,9 @@ public:
     enum Kind { Subtitles, Captions, Descriptions, Chapters, Metadata, None };
     virtual Kind kind() const { return Subtitles; }
     virtual bool isClosedCaptions() const { return false; }
+    virtual bool containsOnlyForcedSubtitles() const { return false; }
+    virtual bool isMainProgramContent() const { return true; }
+    virtual bool isEasyToRead() const { return false; }
 
     virtual AtomicString label() const { return emptyAtom; }
     virtual AtomicString language() const { return emptyAtom; }
index fc56828..d2dcbed 100644 (file)
@@ -49,6 +49,9 @@ public:
 
     virtual InbandTextTrackPrivate::Kind kind() const OVERRIDE;
     virtual bool isClosedCaptions() const OVERRIDE;
+    virtual bool containsOnlyForcedSubtitles() const OVERRIDE;
+    virtual bool isMainProgramContent() const OVERRIDE;
+    virtual bool isEasyToRead() const OVERRIDE;
     virtual AtomicString label() const OVERRIDE;
     virtual AtomicString language() const OVERRIDE;
     virtual bool isDefault() const OVERRIDE;
index 50ede30..6e7e8f3 100644 (file)
@@ -52,8 +52,9 @@ SOFT_LINK_CLASS(AVFoundation, AVPlayerItemLegibleOutput)
 #define AVMediaCharacteristicVisual getAVMediaCharacteristicVisual()
 #define AVMediaCharacteristicAudible getAVMediaCharacteristicAudible()
 #define AVMediaTypeClosedCaption getAVMediaTypeClosedCaption()
-#define AVMediaTypeVideo getAVMediaTypeVideo()
-#define AVMediaTypeAudio getAVMediaTypeAudio()
+#define AVMediaCharacteristicContainsOnlyForcedSubtitles getAVMediaCharacteristicContainsOnlyForcedSubtitles()
+#define AVMediaCharacteristicIsMainProgramContent getAVMediaCharacteristicIsMainProgramContent()
+#define AVMediaCharacteristicEasyToRead getAVMediaCharacteristicEasyToRead()
 
 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeClosedCaption, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicLegible, NSString *)
@@ -62,6 +63,10 @@ SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceCommon, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeSubtitle, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, NSString *)
+SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicContainsOnlyForcedSubtitles, NSString *)
+SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicIsMainProgramContent, NSString *)
+SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicEasyToRead, NSString *)
+
 #define AVMetadataItem getAVMetadataItemClass()
 #define AVPlayerItemLegibleOutput getAVPlayerItemLegibleOutputClass()
 #define AVMediaCharacteristicLegible getAVMediaCharacteristicLegible()
@@ -119,6 +124,30 @@ bool InbandTextTrackPrivateAVFObjC::isClosedCaptions() const
     return [[m_mediaSelectionOption mediaType] isEqualToString:AVMediaTypeClosedCaption];
 }
 
+bool InbandTextTrackPrivateAVFObjC::containsOnlyForcedSubtitles() const
+{
+    if (!m_mediaSelectionOption)
+        return false;
+    
+    return [m_mediaSelectionOption hasMediaCharacteristic:AVMediaCharacteristicContainsOnlyForcedSubtitles];
+}
+
+bool InbandTextTrackPrivateAVFObjC::isMainProgramContent() const
+{
+    if (!m_mediaSelectionOption)
+        return false;
+    
+    return [m_mediaSelectionOption hasMediaCharacteristic:AVMediaCharacteristicIsMainProgramContent];
+}
+
+bool InbandTextTrackPrivateAVFObjC::isEasyToRead() const
+{
+    if (!m_mediaSelectionOption)
+        return false;
+
+    return [m_mediaSelectionOption hasMediaCharacteristic:AVMediaCharacteristicEasyToRead];
+}
+
 AtomicString InbandTextTrackPrivateAVFObjC::label() const
 {
     if (!m_mediaSelectionOption)
index c3f90d5..a9eb627 100644 (file)
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "InternalSettings.h"
 
+#include "CaptionUserPreferences.h"
 #include "Document.h"
 #include "ExceptionCode.h"
 #include "Frame.h"
@@ -34,6 +35,7 @@
 #include "Language.h"
 #include "LocaleToScriptMapping.h"
 #include "Page.h"
+#include "PageGroup.h"
 #include "RuntimeEnabledFeatures.h"
 #include "Settings.h"
 #include "Supplementable.h"
@@ -426,12 +428,16 @@ void InternalSettings::setShouldDisplayTrackKind(const String& kind, bool enable
     InternalSettingsGuardForSettings();
 
 #if ENABLE(VIDEO_TRACK)
+    if (!page())
+        return;
+    CaptionUserPreferences* captionPreferences = page()->group().captionPreferences();
+
     if (equalIgnoringCase(kind, "Subtitles"))
-        settings()->setShouldDisplaySubtitles(enabled);
+        captionPreferences->setUserPrefersSubtitles(enabled);
     else if (equalIgnoringCase(kind, "Captions"))
-        settings()->setShouldDisplayCaptions(enabled);
+        captionPreferences->setUserPrefersCaptions(enabled);
     else if (equalIgnoringCase(kind, "TextDescriptions"))
-        settings()->setShouldDisplayTextDescriptions(enabled);
+        captionPreferences->setUserPrefersTextDescriptions(enabled);
     else
         ec = SYNTAX_ERR;
 #else
@@ -445,12 +451,16 @@ bool InternalSettings::shouldDisplayTrackKind(const String& kind, ExceptionCode&
     InternalSettingsGuardForSettingsReturn(false);
 
 #if ENABLE(VIDEO_TRACK)
+    if (!page())
+        return false;
+    CaptionUserPreferences* captionPreferences = page()->group().captionPreferences();
+
     if (equalIgnoringCase(kind, "Subtitles"))
-        return settings()->shouldDisplaySubtitles();
+        return captionPreferences->userPrefersSubtitles();
     if (equalIgnoringCase(kind, "Captions"))
-        return settings()->shouldDisplayCaptions();
+        return captionPreferences->userPrefersCaptions();
     if (equalIgnoringCase(kind, "TextDescriptions"))
-        return settings()->shouldDisplayTextDescriptions();
+        return captionPreferences->userPrefersTextDescriptions();
 
     ec = SYNTAX_ERR;
     return false;
index 991e867..d817100 100644 (file)
@@ -1,3 +1,13 @@
+2013-03-20  Eric Carlson  <eric.carlson@apple.com>
+
+        Allow ports specific text track menu
+        https://bugs.webkit.org/show_bug.cgi?id=112800
+
+        Reviewed by Dean Jackson.
+
+        * WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in: Add new exports.
+        * win/WebKit.vcproj/WebKitExports.def.in: Ditto.
+
 2013-03-20  Roger Fong  <roger_fong@apple.com>
 
         AppleWin VS2010 build fix.
index e29bff0..00ce93e 100644 (file)
@@ -416,6 +416,8 @@ EXPORTS
         ?setAuthorAndUserStylesEnabled@Settings@WebCore@@QAEX_N@Z
         ?setAcceleratedCompositingEnabled@Settings@WebCore@@QAEX_N@Z
         ?setAutofilled@HTMLInputElement@WebCore@@QAEX_N@Z
+        ?captionPreferences@PageGroup@WebCore@@QAEPAVCaptionUserPreferences@2@XZ
+        ?initGroup@Page@WebCore@@AAEXXZ
 
 #if ENABLE(WORKERS)
         ?workerThreadCount@WorkerThread@WebCore@@SAIXZ
index e29bff0..00ce93e 100644 (file)
@@ -416,6 +416,8 @@ EXPORTS
         ?setAuthorAndUserStylesEnabled@Settings@WebCore@@QAEX_N@Z
         ?setAcceleratedCompositingEnabled@Settings@WebCore@@QAEX_N@Z
         ?setAutofilled@HTMLInputElement@WebCore@@QAEX_N@Z
+        ?captionPreferences@PageGroup@WebCore@@QAEPAVCaptionUserPreferences@2@XZ
+        ?initGroup@Page@WebCore@@AAEXXZ
 
 #if ENABLE(WORKERS)
         ?workerThreadCount@WorkerThread@WebCore@@SAIXZ