Title: [106531] trunk
Revision
106531
Author
eric.carl...@apple.com
Date
2012-02-01 21:10:31 -0800 (Wed, 01 Feb 2012)

Log Message

Consider user's preferred language when choosing text tracks
https://bugs.webkit.org/show_bug.cgi?id=74121

Reviewed by Alexey Proskuryakov.

Source/WebCore: 

Tests: media/track/track-language-preference.html
       media/track/track-prefer-captions.html

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::loadTimerFired): configureTextTracks -> configureNewTextTracks.
(WebCore::HTMLMediaElement::textTracksAreReady): Add more comments.
(WebCore::HTMLMediaElement::textTrackModeChanged): Ditto.
(WebCore::HTMLMediaElement::showingTrackWithSameKind): Minor restructuring.
(WebCore::HTMLMediaElement::userIsInterestedInThisTrackKind): Renamed from userIsInterestedInThisTrack,
    don't consider user's language preference.
(WebCore::HTMLMediaElement::configureTextTrackGroup): New, configure all tracks in a group, 
    considering user's kind and language preferences.
(WebCore::HTMLMediaElement::configureNewTextTracks): New, configure all newly added tracks.
* html/HTMLMediaElement.h:
(WebCore::HTMLMediaElement::TrackGroup::TrackGroup):
(TrackGroup):

* platform/Language.cpp:
(WebCore::canonicalLanguageIdentifier): New, create a canonicalized version of a language string.
(WebCore::bestMatchingLanguage): New, return the language from the list that best matches the 
    specified language.
(WebCore::preferredLanguageFromList): New, return the language in the specified list that best
    matches the user's language preference.
* platform/Language.h:

* testing/Internals.cpp:
(WebCore::Internals::setShouldDisplayTrackType): New, allow DRT to set the track type preference.
(WebCore::Internals::shouldDisplayTrackType): New, allow DRT to read the track type preference.
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests: 

* media/track/track-language-preference-expected.txt: Added.
* media/track/track-language-preference.html: Added.
* media/track/track-prefer-captions-expected.txt: Added.
* media/track/track-prefer-captions.html: Added.
* platform/mac/Skipped:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (106530 => 106531)


--- trunk/LayoutTests/ChangeLog	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/LayoutTests/ChangeLog	2012-02-02 05:10:31 UTC (rev 106531)
@@ -1,3 +1,16 @@
+2012-02-01  Eric Carlson  <eric.carl...@apple.com>
+
+        Consider user's preferred language when choosing text tracks
+        https://bugs.webkit.org/show_bug.cgi?id=74121
+
+        Reviewed by Alexey Proskuryakov.
+
+        * media/track/track-language-preference-expected.txt: Added.
+        * media/track/track-language-preference.html: Added.
+        * media/track/track-prefer-captions-expected.txt: Added.
+        * media/track/track-prefer-captions.html: Added.
+        * platform/mac/Skipped:
+
 2012-02-01  Shinya Kawanaka  <shin...@google.com>
 
         Select attribute of HTMLContentElement should be able be changed dynamically.

Added: trunk/LayoutTests/media/track/track-language-preference-expected.txt (0 => 106531)


--- trunk/LayoutTests/media/track/track-language-preference-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/track/track-language-preference-expected.txt	2012-02-02 05:10:31 UTC (rev 106531)
@@ -0,0 +1,32 @@
+Tests that the user's preferred languages are honored.
+
+**Set track preferences and user preferred languages
+RUN(internals.setShouldDisplayTrackKind(document, 'Captions', true))
+RUN(internals.userPreferredLanguages = ['jp', 'es-ES', 'en', 'fr'])
+
+Test: a track language matches one of the user's preferred languages exactly.
+- creating tracks for: [fr,en,jp].
+EVENT(load)
+EXPECTED (track.readyState == '2') OK
+EXPECTED (track.srclang == 'jp') OK
+
+Test: a track language without locale exactly matches one of the user's preferred languages.
+- creating tracks for: [fr-CH,da].
+EVENT(load)
+EXPECTED (track.readyState == '2') OK
+EXPECTED (track.srclang == 'fr-CH') OK
+
+Test: a track language without locale matches one of the user's preferred languages without locale.
+- creating tracks for: [fr,es-MX].
+EVENT(load)
+EXPECTED (track.readyState == '2') OK
+EXPECTED (track.srclang == 'es-MX') OK
+
+Test: no track language matches any of the user's preferred languages.
+- creating tracks for: [fa,ru,no].
+EVENT(load)
+EXPECTED (track.readyState == '2') OK
+EXPECTED (track.srclang == 'fa') OK
+
+END OF TEST
+

Added: trunk/LayoutTests/media/track/track-language-preference.html (0 => 106531)


--- trunk/LayoutTests/media/track/track-language-preference.html	                        (rev 0)
+++ trunk/LayoutTests/media/track/track-language-preference.html	2012-02-02 05:10:31 UTC (rev 106531)
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+        <style>
+            video { background-color: yellow; width: 320px; height: 240px;}
+        </style>
+        <script src=""
+        <script src=""
+        <script>
+
+            var timer = null;
+            var expectedLanguage;
+            var testList = 
+            {
+                current : -1,
+                tests : 
+                [ 
+                    {
+                        description: "a track language matches one of the user's preferred languages exactly", 
+                        languages : ['fr', 'en', 'jp'], 
+                        expectedLanguage : "jp", 
+                    },
+                    {
+                        description: "a track language without locale exactly matches one of the user's preferred languages", 
+                        languages : ['fr-CH', 'da'], 
+                        expectedLanguage : "fr-CH", 
+                    },
+                    {
+                        description: "a track language without locale matches one of the user's preferred languages without locale", 
+                        languages : ['fr', 'es-MX'], 
+                        expectedLanguage : "es-MX", 
+                    },
+                    {
+                        description: "no track language matches any of the user's preferred languages", 
+                        languages : ['fa', 'ru', 'no'], 
+                        expectedLanguage : "fa", 
+                    },
+                ]
+            };
+
+            function runNextTest()
+            {
+                consoleWrite("");
+                testList.current++;
+                if (testList.current >= testList.tests.length) {
+                    endTest();
+                    return;
+                }
+
+                consoleWrite("<b>Test: </b> <em>"+ testList.tests[testList.current].description + ".</em>");
+                createTrackElements(testList.tests[testList.current].languages);
+            }
+
+            function trackLoaded()
+            {
+                consoleWrite("EVENT(load)");
+                
+                // Don't log the event name because the order of the two events in not predictable.
+                track = event.target;
+                testExpected("track.readyState", HTMLTrackElement.LOADED);
+                testExpected("track.srclang", testList.tests[testList.current].expectedLanguage);
+
+                timer = setTimeout(runNextTest, 200);
+            }
+
+            function setPreferences()
+            {
+                if (!window.internals) {
+                    consoleWrite("<b>** This test only works in DRT! **<" + "/b>");
+                    return;
+                }
+
+                consoleWrite("<i>**Set track preferences and user preferred languages<" + "/i>");
+                run("internals.setShouldDisplayTrackKind(document, 'Captions', true)");
+                run("internals.userPreferredLanguages = ['jp', 'es-ES', 'en', 'fr']");
+            }
+            
+            function createTrackElement(language, src)
+            {
+                var track = document.createElement('track');
+                track.setAttribute('kind', "captions");
+                track.setAttribute('src', src);
+                track.setAttribute('srclang', language);
+                track.setAttribute('onload', 'trackLoaded()');
+                video.appendChild(track);
+            }
+
+            function createTrackElements(languages)
+            {
+                var tracks = document.querySelectorAll('track');
+                for (var ndx = 0; ndx < tracks.length; ++ndx)
+                    video.removeChild(tracks[ndx]);
+
+                consoleWrite("<i>- creating tracks for: [" + languages + "].<" + "/i>");
+                for (var ndx = 0; ndx < languages.length; ++ndx)
+                    createTrackElement(languages[ndx], "captions-webvtt/tc004-webvtt-file.vtt");
+            }
+
+            function setup()
+            {
+                findMediaElement();
+
+                setPreferences("Subtitles", true);
+
+                runNextTest();
+            }
+
+        </script>
+    </head>
+    <body _onload_="setup()">
+        <p>Tests that the user's preferred languages are honored.</p>
+        <video>
+        </video>
+    </body>
+</html>

Added: trunk/LayoutTests/media/track/track-prefer-captions-expected.txt (0 => 106531)


--- trunk/LayoutTests/media/track/track-prefer-captions-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/track/track-prefer-captions-expected.txt	2012-02-02 05:10:31 UTC (rev 106531)
@@ -0,0 +1,25 @@
+Tests that the user preferences for track kind are honored.
+
+**Set preferences so subtitles and descriptions load, but captions do not
+RUN(internals.setShouldDisplayTrackKind(document, 'Subtitles', true))
+EXPECTED (internals.shouldDisplayTrackKind(document, 'Subtitles') == 'true') OK
+RUN(internals.setShouldDisplayTrackKind(document, 'Captions', false))
+EXPECTED (internals.shouldDisplayTrackKind(document, 'Captions') == 'false') OK
+RUN(internals.setShouldDisplayTrackKind(document, 'TextDescriptions', true))
+EXPECTED (internals.shouldDisplayTrackKind(document, 'TextDescriptions') == 'true') OK
+
+**Create track elements dynamically so they aren't processed by the media element until after preferences have been configured.
+- creating 'subtitles' track.
+- creating 'captions' track.
+- creating 'descriptions' track.
+
+EVENT(load)
+EXPECTED (track.readyState == '2') OK
+EXPECTED (track.kind != 'captions') OK
+
+EVENT(load)
+EXPECTED (track.readyState == '2') OK
+EXPECTED (track.kind != 'captions') OK
+
+END OF TEST
+

Added: trunk/LayoutTests/media/track/track-prefer-captions.html (0 => 106531)


--- trunk/LayoutTests/media/track/track-prefer-captions.html	                        (rev 0)
+++ trunk/LayoutTests/media/track/track-prefer-captions.html	2012-02-02 05:10:31 UTC (rev 106531)
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+        <style>
+            video { background-color: yellow; width: 320px; height: 240px;}
+        </style>
+        <script src=""
+        <script src=""
+        <script>
+
+            var timer = null;
+            var counter = 0;
+
+            function trackLoaded()
+            {
+                consoleWrite("EVENT(load)");
+                
+                // Don't log the track type because the load order is not predictable.
+                track = event.target;
+                testExpected("track.readyState", HTMLTrackElement.LOADED);
+                testExpected("track.kind", "captions", "!=");
+
+                // End the test after a brief pause so we allow the third track to load if it will.
+                if (++counter == 2)
+                    timer = setTimeout(endTest, 200);
+
+                consoleWrite("");
+            }
+
+            function setTextTrackPreferences(type, flag)
+            {
+                if (!window.internals) {
+                    consoleWrite("<b>**This test only works in DRT<" + "/b>");
+                    return;
+                }
+
+                run("internals.setShouldDisplayTrackKind(document, '" + type + "', " + flag + ")");
+                testExpected("internals.shouldDisplayTrackKind(document, '" + type + "')", flag);
+            }
+            
+            function createTrackElement(kind, src)
+            {
+                consoleWrite("<i>- creating '" + kind + "' track.<" + "/i>");
+                var track = document.createElement('track');
+                track.setAttribute('kind', kind);
+                track.setAttribute('onload', 'trackLoaded()');
+                track.setAttribute('src', src);
+                video.appendChild(track);
+            }
+
+            function createTrackElements()
+            {
+                var tracks = document.querySelectorAll('track');
+                for (var ndx = 0; ndx < tracks.length; ++ndx)
+                    video.removeChild(tracks[ndx]);
+
+               createTrackElement("subtitles", "captions-webvtt/tc004-webvtt-file.vtt")
+               createTrackElement("captions", "captions-webvtt/tc004-webvtt-file.vtt")
+               createTrackElement("descriptions", "captions-webvtt/tc004-webvtt-file.vtt")
+            }
+
+            function setup()
+            {
+                findMediaElement();
+
+                consoleWrite("<i>**Set preferences so subtitles and descriptions load, but captions do not<" + "/i>");
+                setTextTrackPreferences("Subtitles", true);
+                setTextTrackPreferences("Captions", false);
+                setTextTrackPreferences("TextDescriptions", true);
+
+                // Create track elements dynamically so they aren't processed by the media element
+                // until after we have configured preferences.
+                consoleWrite("<br><i>**Create track elements dynamically so they aren't processed by the media element until after preferences have been configured.<" + "/i>");
+                createTrackElements();
+                consoleWrite("");
+            }
+
+        </script>
+    </head>
+    <body _onload_="setup()">
+        <p>Tests that the user preferences for track kind are honored.</p>
+        <video>
+        </video>
+    </body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (106530 => 106531)


--- trunk/Source/WebCore/ChangeLog	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/ChangeLog	2012-02-02 05:10:31 UTC (rev 106531)
@@ -1,3 +1,41 @@
+2012-02-01  Eric Carlson  <eric.carl...@apple.com>
+
+        Consider user's preferred language when choosing text tracks
+        https://bugs.webkit.org/show_bug.cgi?id=74121
+
+        Reviewed by Alexey Proskuryakov.
+
+        Tests: media/track/track-language-preference.html
+               media/track/track-prefer-captions.html
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::loadTimerFired): configureTextTracks -> configureNewTextTracks.
+        (WebCore::HTMLMediaElement::textTracksAreReady): Add more comments.
+        (WebCore::HTMLMediaElement::textTrackModeChanged): Ditto.
+        (WebCore::HTMLMediaElement::showingTrackWithSameKind): Minor restructuring.
+        (WebCore::HTMLMediaElement::userIsInterestedInThisTrackKind): Renamed from userIsInterestedInThisTrack,
+            don't consider user's language preference.
+        (WebCore::HTMLMediaElement::configureTextTrackGroup): New, configure all tracks in a group, 
+            considering user's kind and language preferences.
+        (WebCore::HTMLMediaElement::configureNewTextTracks): New, configure all newly added tracks.
+        * html/HTMLMediaElement.h:
+        (WebCore::HTMLMediaElement::TrackGroup::TrackGroup):
+        (TrackGroup):
+
+        * platform/Language.cpp:
+        (WebCore::canonicalLanguageIdentifier): New, create a canonicalized version of a language string.
+        (WebCore::bestMatchingLanguage): New, return the language from the list that best matches the 
+            specified language.
+        (WebCore::preferredLanguageFromList): New, return the language in the specified list that best
+            matches the user's language preference.
+        * platform/Language.h:
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::setShouldDisplayTrackType): New, allow DRT to set the track type preference.
+        (WebCore::Internals::shouldDisplayTrackType): New, allow DRT to read the track type preference.
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2012-02-01  Hayato Ito  <hay...@chromium.org>
 
         Change class hierarycy so that ShadowRoot can inherit DocumentFragment.

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (106530 => 106531)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2012-02-02 05:10:31 UTC (rev 106531)
@@ -51,6 +51,7 @@
 #include "HTMLNames.h"
 #include "HTMLSourceElement.h"
 #include "HTMLVideoElement.h"
+#include "Language.h"
 #include "Logging.h"
 #include "MediaController.h"
 #include "MediaControls.h"
@@ -557,7 +558,7 @@
 
 #if ENABLE(VIDEO_TRACK)
     if (m_pendingLoadFlags & TextTrackResource)
-        configureTextTracks();
+        configureNewTextTracks();
 #endif
 
     m_pendingLoadFlags = 0;
@@ -965,6 +966,8 @@
 
 bool HTMLMediaElement::textTracksAreReady() const
 {
+    // 4.8.10.12.1 Text track model
+    // ...
     // The text tracks of a media element are ready if all the text tracks whose mode was not 
     // in the disabled state when the element's resource selection algorithm last started now
     // have a text track readiness state of loaded or failed to load.
@@ -1000,7 +1003,7 @@
             if (trackElement->track() != track)
                 continue;
             
-            // Mark this track as "configured" so configureTextTrack won't change the mode again.
+            // Mark this track as "configured" so configureNewTextTracks won't change the mode again.
             trackElement->setHasBeenConfigured(true);
             if (track->mode() != TextTrack::DISABLED && trackElement->readyState() == HTMLTrackElement::NONE)
                 trackElement->scheduleLoad();
@@ -2240,15 +2243,13 @@
 
 HTMLTrackElement* HTMLMediaElement::showingTrackWithSameKind(HTMLTrackElement* trackElement) const
 {
-    HTMLTrackElement* showingTrack = 0;
-    
     for (Node* node = firstChild(); node; node = node->nextSibling()) {
         if (trackElement == node)
             continue;
         if (!node->hasTagName(trackTag))
             continue;
 
-        showingTrack = static_cast<HTMLTrackElement*>(node);
+        HTMLTrackElement* showingTrack = static_cast<HTMLTrackElement*>(node);
         if (showingTrack->kind() == trackElement->kind() && showingTrack->track()->mode() == TextTrack::SHOWING)
             return showingTrack;
     }
@@ -2315,131 +2316,176 @@
     return true;
 }
 
-bool HTMLMediaElement::userIsInterestedInThisTrack(HTMLTrackElement* trackElement) const
+bool HTMLMediaElement::userIsInterestedInThisTrackKind(String kind) const
 {
-    RefPtr<TextTrack> textTrack = trackElement->track();
-    if (!textTrack)
-        return false;
-
-    String kind = textTrack->kind();
-    if (!TextTrack::isValidKindKeyword(kind))
-        return false;
-
     // If ... the user has indicated an interest in having a track with this text track kind, text track language, ... 
     Settings* settings = document()->settings();
     if (!settings)
         return false;
 
-    if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) {
-        if (kind == TextTrack::subtitlesKeyword() && !settings->shouldDisplaySubtitles())
-            return false;
-        if (kind == TextTrack::captionsKeyword() && !settings->shouldDisplayCaptions())
-            return false;
-        return userIsInterestedInThisLanguage(trackElement->srclang());
-    }
+    if (kind == TextTrack::subtitlesKeyword())
+        return settings->shouldDisplaySubtitles();
+    if (kind == TextTrack::captionsKeyword())
+        return settings->shouldDisplayCaptions();
+    if (kind == TextTrack::descriptionsKeyword())
+        return settings->shouldDisplayTextDescriptions();
 
-    if (kind == TextTrack::descriptionsKeyword()) {
-        if (!settings->shouldDisplayTextDescriptions())
-            return false;
-        return userIsInterestedInThisLanguage(trackElement->srclang());
-    }
-    
     return false;
 }
 
-void HTMLMediaElement::configureTextTrack(HTMLTrackElement* trackElement)
+void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group) const
 {
-#if !LOG_DISABLED
-    if (trackElement->hasTagName(trackTag)) {
-        KURL url = ""
-        LOG(Media, "HTMLMediaElement::configureTextTrack - 'src' is %s", urlForLogging(url).utf8().data());
+    ASSERT(group.tracks.size());
+
+    String bestMatchingLanguage;
+    if (group.hasSrcLang) {
+        Vector<String> languages;
+        languages.reserveInitialCapacity(group.tracks.size());
+        for (size_t i = 0; i < group.tracks.size(); ++i) {
+            String srcLanguage = group.tracks[i]->track()->language();
+            if (srcLanguage.length())
+                languages.append(srcLanguage);
+        }
+        bestMatchingLanguage = preferredLanguageFromList(languages);
     }
-#endif
 
-    // 4.8.10.12.3 Sourcing out-of-band text tracks
-    
-    // When a text track corresponding to a track element is added to a media element's list of text tracks,
-    // the user agent must set the text track mode appropriately, as determined by the following conditions:
-    RefPtr<TextTrack> textTrack = trackElement->track();
-    if (!textTrack)
-        return;
-    
-    TextTrack::Mode mode = TextTrack::HIDDEN;
-    HTMLTrackElement* trackElementCurrentlyShowing = showingTrackWithSameKind(trackElement);
-    String kind = textTrack->kind();
-    bool hideDefaultTrack = false;
+    // First, find the track in the group that should be enabled (if any).
+    HTMLTrackElement* trackElementToEnable = 0;
+    HTMLTrackElement* defaultTrack = 0;
+    HTMLTrackElement* fallbackTrack = 0;
+    for (size_t i = 0; !trackElementToEnable && i < group.tracks.size(); ++i) {
+        HTMLTrackElement* trackElement = group.tracks[i];
+        RefPtr<TextTrack> textTrack = trackElement->track();
 
-    if (userIsInterestedInThisTrack(trackElement)) {
-        if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) {
-            // * If the text track kind is subtitles or captions and the user has indicated an interest in having a
+        if (userIsInterestedInThisTrackKind(textTrack->kind())) {
+            // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
             // track with this text track kind, text track language, and text track label enabled, and there is no
             // other text track in the media element's list of text tracks with a text track kind of either subtitles
             // or captions whose text track mode is showing
-            hideDefaultTrack = trackElementCurrentlyShowing && trackElementCurrentlyShowing->track()->showingByDefault();
-            if (!trackElementCurrentlyShowing || hideDefaultTrack) {
-                //    Let the text track mode be showing.
-                //    If there is a text track in the media element's list of text tracks whose text track mode is showing 
-                //    by default, the user agent must furthermore change that text track's text track mode to hidden.
-                mode = TextTrack::SHOWING;
-            }
-        } else if (kind == TextTrack::descriptionsKeyword()) {
-            // * If the text track kind is descriptions and the user has indicated an interest in having text 
-            // descriptions with this text track language and text track label enabled, and there is no other text 
-            // track in the media element's list of text tracks with a text track kind of descriptions whose text 
-            // track mode is showing
-            hideDefaultTrack = trackElementCurrentlyShowing && trackElementCurrentlyShowing->track()->showingByDefault();
-            if (!trackElementCurrentlyShowing || hideDefaultTrack) {
-                //    Let the text track mode be showing.
-                //    If there is a text track in the media element's list of text tracks whose text track mode is showing 
-                //    by default, the user agent must furthermore change that text track's text track mode to hidden.
-                mode = TextTrack::SHOWING;
-            }
-        } else if (kind == TextTrack::chaptersKeyword()) {
+            // ...
             // * If the text track kind is chapters and the text track language is one that the user agent has reason
             // to believe is appropriate for the user, and there is no other text track in the media element's list of
             // text tracks with a text track kind of chapters whose text track mode is showing
             //    Let the text track mode be showing.
-            if (!trackElementCurrentlyShowing)
-                mode = TextTrack::SHOWING;
+            if (bestMatchingLanguage.length()) {
+                if (textTrack->language() == bestMatchingLanguage)
+                    trackElementToEnable = trackElement;
+            } else if (trackElement->isDefault()) {
+                // The user is interested in this type of track, but their language preference doesn't match any track so we will
+                // enable the 'default' track.
+                defaultTrack = trackElement;
+            }
+
+            // Remember the first track that doesn't match language or have 'default' to potentially use as fallback.
+            if (!fallbackTrack)
+                fallbackTrack = trackElement;
+        } else if (!group.visibleTrack && !defaultTrack && trackElement->isDefault()) {
+            // * If the track element has a default attribute specified, and there is no other text track in the media
+            // element's list of text tracks whose text track mode is showing or showing by default
+            //    Let the text track mode be showing by default.
+            defaultTrack = trackElement;
         }
-    } else if (!trackElementCurrentlyShowing && trackElement->isDefault()) {
-        // * If the track element has a default attribute specified, and there is no other text track in the media
-        // element's list of text tracks whose text track mode is showing or showing by default
-        //    Let the text track mode be showing by default.
-        mode = TextTrack::SHOWING;
-        textTrack->setShowingByDefault(false);
-    } else {
-        // Otherwise
-        //    Let the text track mode be disabled.
-        mode = TextTrack::DISABLED;
     }
 
-    ExceptionCode unusedException;
-    if (hideDefaultTrack) {
-        trackElementCurrentlyShowing->track()->setMode(TextTrack::HIDDEN, unusedException);
-        trackElementCurrentlyShowing->track()->setShowingByDefault(false);
+    if (!trackElementToEnable && defaultTrack)
+        trackElementToEnable = defaultTrack;
+
+    // If no track matches the user's preferred language and non was marked 'default', enable the first track
+    // because the user has explicitly stated a preference for this kind of track.
+    if (!trackElementToEnable && fallbackTrack)
+        trackElementToEnable = fallbackTrack;
+
+    for (size_t i = 0; i < group.tracks.size(); ++i) {
+        HTMLTrackElement* trackElement = group.tracks[i];
+        RefPtr<TextTrack> textTrack = trackElement->track();
+        ExceptionCode unusedException;
+        
+        if (trackElementToEnable == trackElement) {
+            textTrack->setMode(TextTrack::SHOWING, unusedException);
+            if (defaultTrack == trackElement)
+                textTrack->setShowingByDefault(true);
+        } else {
+            if (textTrack->showingByDefault()) {
+                // If there is a text track in the media element's list of text tracks whose text track
+                // mode is showing by default, the user agent must furthermore change that text track's
+                // text track mode to hidden.
+                textTrack->setShowingByDefault(false);
+                textTrack->setMode(TextTrack::HIDDEN, unusedException);
+            } else
+                textTrack->setMode(TextTrack::DISABLED, unusedException);
+        }
     }
 
-    textTrack->setMode(mode, unusedException);
+    if (trackElementToEnable && group.defaultTrack && group.defaultTrack != trackElementToEnable) {
+        RefPtr<TextTrack> textTrack = group.defaultTrack->track();
+        if (textTrack && textTrack->showingByDefault()) {
+            ExceptionCode unusedException;
+            textTrack->setShowingByDefault(false);
+            textTrack->setMode(TextTrack::HIDDEN, unusedException);
+        }
+    }
 }
- 
-void HTMLMediaElement::configureTextTracks()
+
+void HTMLMediaElement::configureNewTextTracks()
 {
+    TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
+    TrackGroup descriptionTracks(TrackGroup::Description);
+    TrackGroup chapterTracks(TrackGroup::Chapter);
+    TrackGroup metadataTracks(TrackGroup::Metadata);
+    TrackGroup otherTracks(TrackGroup::Other);
+
     for (Node* node = firstChild(); node; node = node->nextSibling()) {
         if (!node->hasTagName(trackTag))
             continue;
+
         HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
+        RefPtr<TextTrack> textTrack = trackElement->track();
+        if (!textTrack)
+            continue;
+
+        String kind = textTrack->kind();
+        TrackGroup* currentGroup;
+        if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
+            currentGroup = &captionAndSubtitleTracks;
+        else if (kind == TextTrack::descriptionsKeyword())
+            currentGroup = &descriptionTracks;
+        else if (kind == TextTrack::chaptersKeyword())
+            currentGroup = &chapterTracks;
+        else if (kind == TextTrack::metadataKeyword())
+            currentGroup = &metadataTracks;
+        else
+            currentGroup = &otherTracks;
+
+        if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::SHOWING)
+            currentGroup->visibleTrack = trackElement;
+        if (!currentGroup->defaultTrack && trackElement->isDefault())
+            currentGroup->defaultTrack = trackElement;
+
+        // Do not add this track to the group if it has already been automatically configured
+        // as we only want to call configureTextTrack once per track so that adding another 
+        // track after the initial configuration doesn't reconfigure every track - only those 
+        // that should be changed by the new addition. For example all metadata tracks are 
+        // disabled by default, and we don't want a track that has been enabled by script 
+        // to be disabled automatically when a new metadata track is added later.
+        if (trackElement->hasBeenConfigured())
+            continue;
         
-        // Only call configureTextTrack once per track so that adding another track after
-        // the initial configuration doesn't reconfigure every track, only those that should
-        // be changed by the new addition. For example all metadata tracks are disabled by 
-        // default, and we don't want a track that has been enabled by script to be disabled
-        // automatically when a new track element is added later.
-        if (!trackElement->hasBeenConfigured())
-            configureTextTrack(trackElement);
+        if (textTrack->language().length())
+            currentGroup->hasSrcLang = true;
+        currentGroup->tracks.append(trackElement);
     }
+    
+    if (captionAndSubtitleTracks.tracks.size())
+        configureTextTrackGroup(captionAndSubtitleTracks);
+    if (descriptionTracks.tracks.size())
+        configureTextTrackGroup(descriptionTracks);
+    if (chapterTracks.tracks.size())
+        configureTextTrackGroup(chapterTracks);
+    if (metadataTracks.tracks.size())
+        configureTextTrackGroup(metadataTracks);
+    if (otherTracks.tracks.size())
+        configureTextTrackGroup(otherTracks);
 }
-
 #endif
 
 bool HTMLMediaElement::havePotentialSourceChild()

Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (106530 => 106531)


--- trunk/Source/WebCore/html/HTMLMediaElement.h	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h	2012-02-02 05:10:31 UTC (rev 106531)
@@ -207,8 +207,29 @@
     virtual void trackWasAdded(HTMLTrackElement*);
     virtual void trackWasRemoved(HTMLTrackElement*);
 
-    void configureTextTrack(HTMLTrackElement*);
-    void configureTextTracks();
+    struct TrackGroup {
+        enum GroupKind { CaptionsAndSubtitles, Description, Chapter, Metadata, Other };
+
+        TrackGroup(GroupKind kind)
+            : visibleTrack(0)
+            , defaultTrack(0)
+            , kind(kind)
+            , hasSrcLang(false)
+        {
+        }
+
+        Vector<HTMLTrackElement*> tracks;
+        HTMLTrackElement* visibleTrack;
+        HTMLTrackElement* defaultTrack;
+        GroupKind kind;
+        bool hasSrcLang;
+    };
+
+    void configureTextTrackGroupForLanguage(const TrackGroup&) const;
+    void configureNewTextTracks();
+    void configureTextTrackGroup(const TrackGroup&) const;
+
+    bool userIsInterestedInThisTrackKind(String) const;
     bool textTracksAreReady() const;
     void configureTextTrackDisplay();
 
@@ -401,7 +422,6 @@
 #if ENABLE(VIDEO_TRACK)
     void updateActiveTextTrackCues(float);
     bool userIsInterestedInThisLanguage(const String&) const;
-    bool userIsInterestedInThisTrack(HTMLTrackElement*) const;
     HTMLTrackElement* showingTrackWithSameKind(HTMLTrackElement*) const;
 
     bool ignoreTrackDisplayUpdateRequests() const { return m_ignoreTrackDisplayUpdate > 0; }

Modified: trunk/Source/WebCore/platform/Language.cpp (106530 => 106531)


--- trunk/Source/WebCore/platform/Language.cpp	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/platform/Language.cpp	2012-02-02 05:10:31 UTC (rev 106531)
@@ -85,4 +85,62 @@
     return platformUserPreferredLanguages();
 }
 
+static String canonicalLanguageIdentifier(const String& languageCode)
+{
+    String lowercaseLanguageCode = languageCode.lower();
+    
+    if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_')
+        lowercaseLanguageCode.replace(2, 1, "-");
+
+    return lowercaseLanguageCode;
 }
+
+static String bestMatchingLanguage(const String& language, const Vector<String>& languageList)
+{
+    bool canMatchLanguageOnly = (language.length() == 2 || (language.length() >= 3 && language[2] == '-'));
+    String languageWithoutLocaleMatch;
+    String languageMatchButNotLocale;
+
+    for (size_t i = 0; i < languageList.size(); ++i) {
+        String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]);
+
+        if (language == canonicalizedLanguageFromList)
+            return languageList[i];
+
+        if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) {
+            if (language[0] == canonicalizedLanguageFromList[0] && language[1] == canonicalizedLanguageFromList[1]) {
+                if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2)
+                    languageWithoutLocaleMatch = languageList[i];
+                if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3)
+                    languageMatchButNotLocale = languageList[i];
+            }
+        }
+    }
+
+    // If we have both a language-only match and a languge-but-not-locale match, return the 
+    // languge-only match as is considered a "better" match. For example, if the list
+    // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en".
+    if (languageWithoutLocaleMatch.length())
+        return languageWithoutLocaleMatch;
+
+    if (languageMatchButNotLocale.length())
+        return languageMatchButNotLocale;
+    
+    return emptyString();
+}
+
+String preferredLanguageFromList(const Vector<String>& languageList)
+{
+    Vector<String> preferredLanguages = userPreferredLanguages();
+
+    for (size_t i = 0; i < preferredLanguages.size(); ++i) {
+        String bestMatch = bestMatchingLanguage(canonicalLanguageIdentifier(preferredLanguages[i]), languageList);
+
+        if (bestMatch.length())
+            return bestMatch;
+    }
+
+    return emptyString();
+}
+    
+}

Modified: trunk/Source/WebCore/platform/Language.h (106530 => 106531)


--- trunk/Source/WebCore/platform/Language.h	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/platform/Language.h	2012-02-02 05:10:31 UTC (rev 106531)
@@ -34,6 +34,7 @@
 String defaultLanguage();
 Vector<String> userPreferredLanguages();
 void overrideUserPreferredLanguages(const Vector<String>&);
+String preferredLanguageFromList(const Vector<String>&);
 
 // The observer function will be called when system language changes.
 typedef void (*LanguageChangeObserverFunction)(void* context);

Modified: trunk/Source/WebCore/testing/Internals.cpp (106530 => 106531)


--- trunk/Source/WebCore/testing/Internals.cpp	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/testing/Internals.cpp	2012-02-02 05:10:31 UTC (rev 106531)
@@ -491,4 +491,53 @@
     WebCore::overrideUserPreferredLanguages(languages);
 }
 
+void Internals::setShouldDisplayTrackKind(Document* document, const String& kind, bool enabled, ExceptionCode& ec)
+{
+    if (!document || !document->frame() || !document->frame()->settings()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    
+#if ENABLE(VIDEO_TRACK)
+    Settings* settings = document->frame()->settings();
+    
+    if (equalIgnoringCase(kind, "Subtitles"))
+        settings->setShouldDisplaySubtitles(enabled);
+    else if (equalIgnoringCase(kind, "Captions"))
+        settings->setShouldDisplayCaptions(enabled);
+    else if (equalIgnoringCase(kind, "TextDescriptions"))
+        settings->setShouldDisplayTextDescriptions(enabled);
+    else
+        ec = SYNTAX_ERR;
+#else
+    UNUSED_PARAM(kind);
+    UNUSED_PARAM(enabled);
+#endif
 }
+
+bool Internals::shouldDisplayTrackKind(Document* document, const String& kind, ExceptionCode& ec)
+{
+    if (!document || !document->frame() || !document->frame()->settings()) {
+        ec = INVALID_ACCESS_ERR;
+        return false;
+    }
+    
+#if ENABLE(VIDEO_TRACK)
+    Settings* settings = document->frame()->settings();
+    
+    if (equalIgnoringCase(kind, "Subtitles"))
+        return settings->shouldDisplaySubtitles();
+    if (equalIgnoringCase(kind, "Captions"))
+        return settings->shouldDisplayCaptions();
+    if (equalIgnoringCase(kind, "TextDescriptions"))
+        return settings->shouldDisplayTextDescriptions();
+
+    ec = SYNTAX_ERR;
+    return false;
+#else
+    UNUSED_PARAM(kind);
+    return false;
+#endif
+}
+    
+}

Modified: trunk/Source/WebCore/testing/Internals.h (106530 => 106531)


--- trunk/Source/WebCore/testing/Internals.h	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/testing/Internals.h	2012-02-02 05:10:31 UTC (rev 106531)
@@ -102,6 +102,9 @@
     Vector<String> userPreferredLanguages() const;
     void setUserPreferredLanguages(const Vector<String>&);
 
+    void setShouldDisplayTrackKind(Document*, const String& kind, bool, ExceptionCode&);
+    bool shouldDisplayTrackKind(Document*, const String& kind, ExceptionCode&);
+
     static const char* internalsId;
 
     InternalSettings* settings() const { return m_settings.get(); }

Modified: trunk/Source/WebCore/testing/Internals.idl (106530 => 106531)


--- trunk/Source/WebCore/testing/Internals.idl	2012-02-02 04:21:29 UTC (rev 106530)
+++ trunk/Source/WebCore/testing/Internals.idl	2012-02-02 05:10:31 UTC (rev 106531)
@@ -73,6 +73,10 @@
         long lastSpellCheckRequestSequence(in Document document) raises (DOMException);
         long lastSpellCheckProcessedSequence(in Document document) raises (DOMException);
 
+#if defined(ENABLE_VIDEO_TRACK) && ENABLE_VIDEO_TRACK
+        void setShouldDisplayTrackKind(in Document document, in DOMString kind, in boolean enabled) raises (DOMException);
+        boolean shouldDisplayTrackKind(in Document document, in DOMString trackKind) raises (DOMException);
+#endif
         attribute [Custom] Array userPreferredLanguages;
 
         readonly attribute InternalSettings settings;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to