Cleanup text track selection logic

https://bugs.webkit.org/show_bug.cgi?id=113062

Reviewed by Jer Noble.

No new tests, covered by existing tests.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::userIsInterestedInThisTrackKind): Remove.
(WebCore::HTMLMediaElement::configureTextTrackGroup): Don't look at track attributes directly,
    use captionPreferences->textTrackSelectionScore to calculate track rank.
(WebCore::HTMLMediaElement::setClosedCaptionsVisible): Set m_processingPreferenceChange here
    instead of in captionPreferencesChanged.
(WebCore::HTMLMediaElement::captionPreferencesChanged): Don't suppress calls to setClosedCaptionsVisible,
    existing code already makes sure we don't do unnecessary work.
* html/HTMLMediaElement.h:

* html/shadow/MediaControlElements.cpp:
(WebCore::MediaControlTextTrackContainerElement::updateDisplay): Drive by cleanup, don't
    process inactive cues.

* html/shadow/MediaControlsApple.cpp:
(WebCore::MediaControlsApple::changedClosedCaptionsVisibility): Call resetTrackListMenu instead
    of updateDisplay so we only mark the menu as needing a recalculation and do the work when
    it is displayed.

* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::shouldShowCaptions): When in testing mode, return true if
    the caption or subtitle preference has been set.
(WebCore::CaptionUserPreferences::setShouldShowCaptions): In testing mode, clear the caption
    and subtitle preference when passed false.
(WebCore::CaptionUserPreferences::textTrackSelectionScore): Calculate the track score based on
    track type preference and preferred language.
(WebCore::CaptionUserPreferences::textTrackLanguageSelectionScore): Score a track according to
    the language presence and position in the preferred languages list.
* page/CaptionUserPreferences.h:

* page/CaptionUserPreferencesMac.h:
* page/CaptionUserPreferencesMac.mm:
(WebCore::CaptionUserPreferencesMac::textTrackSelectionScore): Calculate track language score 
    according to user preferences.

* platform/Language.cpp:
(WebCore::indexOfBestMatchingLanguageInList): Repurposed the static bestMatchingLanguage
    function to return the location of a language in a Vector.
(WebCore::preferredLanguageFromList): Removed.
* platform/Language.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@146647 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent d0b4bad6
2013-03-22 Eric Carlson <eric.carlson@apple.com>
Cleanup text track selection logic
https://bugs.webkit.org/show_bug.cgi?id=113062
Reviewed by Jer Noble.
No new tests, covered by existing tests.
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::userIsInterestedInThisTrackKind): Remove.
(WebCore::HTMLMediaElement::configureTextTrackGroup): Don't look at track attributes directly,
use captionPreferences->textTrackSelectionScore to calculate track rank.
(WebCore::HTMLMediaElement::setClosedCaptionsVisible): Set m_processingPreferenceChange here
instead of in captionPreferencesChanged.
(WebCore::HTMLMediaElement::captionPreferencesChanged): Don't suppress calls to setClosedCaptionsVisible,
existing code already makes sure we don't do unnecessary work.
* html/HTMLMediaElement.h:
* html/shadow/MediaControlElements.cpp:
(WebCore::MediaControlTextTrackContainerElement::updateDisplay): Drive by cleanup, don't
process inactive cues.
* html/shadow/MediaControlsApple.cpp:
(WebCore::MediaControlsApple::changedClosedCaptionsVisibility): Call resetTrackListMenu instead
of updateDisplay so we only mark the menu as needing a recalculation and do the work when
it is displayed.
* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::shouldShowCaptions): When in testing mode, return true if
the caption or subtitle preference has been set.
(WebCore::CaptionUserPreferences::setShouldShowCaptions): In testing mode, clear the caption
and subtitle preference when passed false.
(WebCore::CaptionUserPreferences::textTrackSelectionScore): Calculate the track score based on
track type preference and preferred language.
(WebCore::CaptionUserPreferences::textTrackLanguageSelectionScore): Score a track according to
the language presence and position in the preferred languages list.
* page/CaptionUserPreferences.h:
* page/CaptionUserPreferencesMac.h:
* page/CaptionUserPreferencesMac.mm:
(WebCore::CaptionUserPreferencesMac::textTrackSelectionScore): Calculate track language score
according to user preferences.
* platform/Language.cpp:
(WebCore::indexOfBestMatchingLanguageInList): Repurposed the static bestMatchingLanguage
function to return the location of a language in a Vector.
(WebCore::preferredLanguageFromList): Removed.
* platform/Language.h:
2013-03-22 ChangSeok Oh <changseok.oh@collabora.com>
Build fix for TransformationMatrix
......@@ -3079,59 +3079,27 @@ bool HTMLMediaElement::userPrefersCaptions() const
return captionPreferences->userHasCaptionPreferences() && captionPreferences->shouldShowCaptions();
}
bool HTMLMediaElement::userIsInterestedInThisTrackKind(String kind) const
{
if (m_disableCaptions)
return false;
Page* page = document()->page();
if (!page)
return false;
CaptionUserPreferences* captionPreferences = page->group().captionPreferences();
bool userPrefersCaptionsOrSubtitles = m_closedCaptionsVisible || userPrefersCaptions();
if (kind == TextTrack::subtitlesKeyword())
return captionPreferences->userPrefersSubtitles() || userPrefersCaptionsOrSubtitles;
if (kind == TextTrack::captionsKeyword())
return captionPreferences->userPrefersCaptions() || userPrefersCaptionsOrSubtitles;
if (kind == TextTrack::descriptionsKeyword())
return captionPreferences->userPrefersTextDescriptions() || userPrefersCaptionsOrSubtitles;
return false;
}
void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
{
ASSERT(group.tracks.size());
String bestMatchingLanguage;
if (group.hasSrcLang && document()->page()) {
Vector<String> trackLanguages;
trackLanguages.reserveInitialCapacity(group.tracks.size());
for (size_t i = 0; i < group.tracks.size(); ++i) {
String srcLanguage = group.tracks[i]->language();
if (srcLanguage.length())
trackLanguages.append(srcLanguage);
}
bestMatchingLanguage = preferredLanguageFromList(trackLanguages, document()->page()->group().captionPreferences()->preferredLanguages());
}
Page* page = document()->page();
CaptionUserPreferences* captionPreferences = page? page->group().captionPreferences() : 0;
// First, find the track in the group that should be enabled (if any).
Vector<RefPtr<TextTrack> > currentlyEnabledTracks;
RefPtr<TextTrack> trackToEnable;
RefPtr<TextTrack> defaultTrack;
RefPtr<TextTrack> fallbackTrack;
int highestTrackScore = 0;
for (size_t i = 0; i < group.tracks.size(); ++i) {
RefPtr<TextTrack> textTrack = group.tracks[i];
if (m_processingPreferenceChange && textTrack->mode() == TextTrack::showingKeyword())
currentlyEnabledTracks.append(textTrack);
if (trackToEnable)
continue;
if (userIsInterestedInThisTrackKind(textTrack->kind())) {
int trackScore = captionPreferences ? captionPreferences->textTrackSelectionScore(textTrack.get()) : 0;
if (trackScore) {
// * 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
......@@ -3141,17 +3109,14 @@ void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
// 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 (bestMatchingLanguage.length()) {
if (textTrack->language() == bestMatchingLanguage)
trackToEnable = textTrack;
} else if (textTrack->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 = textTrack;
if (trackScore > highestTrackScore) {
highestTrackScore = trackScore;
trackToEnable = textTrack;
}
// Remember the first track that doesn't match language or have 'default' to potentially use as fallback.
if (!fallbackTrack)
if (!defaultTrack && textTrack->isDefault())
defaultTrack = textTrack;
if (!defaultTrack && !fallbackTrack)
fallbackTrack = textTrack;
} else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) {
// * If the track element has a default attribute specified, and there is no other text track in the media
......@@ -4328,6 +4293,7 @@ void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
#if ENABLE(VIDEO_TRACK)
if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
m_processingPreferenceChange = true;
m_disableCaptions = !m_closedCaptionsVisible;
markCaptionAndSubtitleTracksAsUnconfigured();
......@@ -4529,12 +4495,7 @@ void HTMLMediaElement::captionPreferencesChanged()
if (hasMediaControls())
mediaControls()->textTrackPreferencesChanged();
bool prefersCaptions = userPrefersCaptions();
if (m_disableCaptions == !prefersCaptions)
return;
m_processingPreferenceChange = true;
setClosedCaptionsVisible(prefersCaptions);
setClosedCaptionsVisible(userPrefersCaptions());
}
void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured()
......
......@@ -272,7 +272,6 @@ public:
static int textTracksIndexNotFound() { return -2; }
bool userPrefersCaptions() const;
bool userIsInterestedInThisTrackKind(String) const;
bool textTracksAreReady() const;
void configureTextTrackDisplay();
void updateTextTrackDisplay();
......
......@@ -1254,7 +1254,7 @@ void MediaControlTextTrackContainerElement::updateDisplay()
TextTrackCue* cue = activeCues[i].data();
ASSERT(cue->isActive());
if (!cue->track() || !cue->track()->isRendered())
if (!cue->track() || !cue->track()->isRendered() || !cue->isActive())
continue;
RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size());
......
......@@ -306,7 +306,7 @@ void MediaControlsApple::changedClosedCaptionsVisibility()
{
MediaControls::changedClosedCaptionsVisibility();
if (m_closedCaptionsTrackList)
m_closedCaptionsTrackList->updateDisplay();
m_closedCaptionsTrackList->resetTrackListMenu();
}
void MediaControlsApple::reset()
......
......@@ -51,7 +51,10 @@ CaptionUserPreferences::~CaptionUserPreferences()
bool CaptionUserPreferences::shouldShowCaptions() const
{
return m_testingMode ? m_shouldShowCaptions : false;
if (!m_testingMode)
return false;
return m_shouldShowCaptions || userPrefersCaptions() || userPrefersSubtitles();
}
void CaptionUserPreferences::timerFired(Timer<CaptionUserPreferences>*)
......@@ -72,6 +75,10 @@ void CaptionUserPreferences::notify()
void CaptionUserPreferences::setShouldShowCaptions(bool preference)
{
m_shouldShowCaptions = preference;
if (m_testingMode && !preference) {
setUserPrefersCaptions(false);
setUserPrefersSubtitles(false);
}
notify();
}
......@@ -185,6 +192,36 @@ Vector<RefPtr<TextTrack> > CaptionUserPreferences::sortedTrackListForMenu(TextTr
return tracksForMenu;
}
int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track) const
{
int trackScore = 0;
if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
return trackScore;
if (track->kind() == TextTrack::subtitlesKeyword() && userPrefersSubtitles())
trackScore = 1;
else if (track->kind() == TextTrack::captionsKeyword() && userPrefersCaptions())
trackScore = 1;
return trackScore + textTrackLanguageSelectionScore(track);
}
int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track) const
{
if (track->language().isEmpty())
return 0;
Vector<String> languages = preferredLanguages();
size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), languages);
if (languageMatchIndex >= languages.size())
return 0;
// Matching a track language is more important than matching track type, so this multiplier must be
// greater than the maximum value returned by textTrackSelectionScore.
return (languages.size() - languageMatchIndex) * 10;
}
}
#endif // ENABLE(VIDEO_TRACK)
......@@ -49,6 +49,9 @@ public:
virtual bool shouldShowCaptions() const;
virtual void setShouldShowCaptions(bool);
virtual int textTrackSelectionScore(TextTrack*) const;
virtual int textTrackLanguageSelectionScore(TextTrack*) const;
virtual bool userPrefersCaptions() const;
virtual void setUserPrefersCaptions(bool);
......
......@@ -60,6 +60,7 @@ public:
#endif
virtual int textTrackSelectionScore(TextTrack*) const OVERRIDE;
virtual Vector<RefPtr<TextTrack> > sortedTrackListForMenu(TextTrackList*) OVERRIDE;
virtual String displayNameForTrack(TextTrack*) const OVERRIDE;
......
......@@ -598,6 +598,40 @@ static String languageIdentifier(const String& languageCode)
return lowercaseLanguageCode;
}
int CaptionUserPreferencesMac::textTrackSelectionScore(TextTrack* track) const
{
if (!shouldShowCaptions())
return 0;
if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
return 0;
if (track->containsOnlyForcedSubtitles())
return 0;
if (!track->isMainProgramContent())
return 0;
int trackScore = 0;
if (userPrefersCaptions()) {
// When the user prefers accessiblity tracks, rank is SDH, then CC, then subtitles.
if (track->kind() == track->subtitlesKeyword())
trackScore = 1;
else if (track->isClosedCaptions())
trackScore = 2;
else
trackScore = 3;
} else {
// When the user prefers translation tracks, rank is subtitles, then SDH, then CC tracks.
if (track->kind() == track->subtitlesKeyword())
trackScore = 3;
else if (!track->isClosedCaptions())
trackScore = 2;
else
trackScore = 1;
}
return trackScore + textTrackLanguageSelectionScore(track);
}
static bool textTrackCompare(const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b)
{
String preferredLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(defaultLanguage()));
......
/*
* Copyright (C) 2010 Apple Inc. All rights reserved.
* Copyright (C) 2010, 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
......@@ -105,50 +105,44 @@ static String canonicalLanguageIdentifier(const String& languageCode)
return lowercaseLanguageCode;
}
static String bestMatchingLanguage(const String& language, const Vector<String>& languageList)
size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList)
{
bool canMatchLanguageOnly = (language.length() == 2 || (language.length() >= 3 && language[2] == '-'));
String languageWithoutLocaleMatch;
String languageMatchButNotLocale;
size_t languageWithoutLocaleMatchIndex = 0;
size_t languageMatchButNotLocaleMatchIndex = 0;
bool canMatchLanguageOnly = (language.length() == 2 || (language.length() >= 3 && language[2] == '-'));
for (size_t i = 0; i < languageList.size(); ++i) {
String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]);
if (language == canonicalizedLanguageFromList)
return languageList[i];
return i;
if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) {
if (language[0] == canonicalizedLanguageFromList[0] && language[1] == canonicalizedLanguageFromList[1]) {
if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2)
if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) {
languageWithoutLocaleMatch = languageList[i];
if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3)
languageWithoutLocaleMatchIndex = i;
}
if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) {
languageMatchButNotLocale = languageList[i];
languageMatchButNotLocaleMatchIndex = i;
}
}
}
}
// If we have both a language-only match and a languge-but-not-locale match, return the
// 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;
return languageWithoutLocaleMatchIndex;
if (languageMatchButNotLocale.length())
return languageMatchButNotLocale;
return emptyString();
}
return languageMatchButNotLocaleMatchIndex;
String preferredLanguageFromList(const Vector<String>& languageList, const Vector<String> preferredLanguages)
{
for (size_t i = 0; i < preferredLanguages.size(); ++i) {
String bestMatch = bestMatchingLanguage(canonicalLanguageIdentifier(preferredLanguages[i]), languageList);
if (bestMatch.length())
return bestMatch;
}
return emptyString();
return languageList.size();
}
String displayNameForLanguageLocale(const String& localeName)
......
/*
* Copyright (C) 2003, 2006, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2003, 2006, 2010, 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
......@@ -35,7 +35,7 @@ String defaultLanguage();
Vector<String> userPreferredLanguages();
Vector<String> userPreferredLanguagesOverride();
void overrideUserPreferredLanguages(const Vector<String>&);
String preferredLanguageFromList(const Vector<String>& languageList, const Vector<String> preferredLanguages);
size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList);
// The observer function will be called when system language changes.
typedef void (*LanguageChangeObserverFunction)(void* context);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment