1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27 28#if ENABLE(VIDEO_TRACK) 29 30#include "CaptionUserPreferences.h" 31#include "DOMWrapperWorld.h" 32#include "Page.h" 33#include "PageGroup.h" 34#include "Settings.h" 35#include "TextTrackList.h" 36#include "UserContentController.h" 37#include "UserStyleSheetTypes.h" 38 39namespace WebCore { 40 41CaptionUserPreferences::CaptionUserPreferences(PageGroup& group) 42 : m_pageGroup(group) 43 , m_displayMode(ForcedOnly) 44 , m_timer(this, &CaptionUserPreferences::timerFired) 45 , m_testingMode(false) 46 , m_havePreferences(false) 47{ 48} 49 50CaptionUserPreferences::~CaptionUserPreferences() 51{ 52} 53 54void CaptionUserPreferences::timerFired(Timer<CaptionUserPreferences>&) 55{ 56 captionPreferencesChanged(); 57} 58 59void CaptionUserPreferences::notify() 60{ 61 m_havePreferences = true; 62 if (!m_timer.isActive()) 63 m_timer.startOneShot(0); 64} 65 66CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferences::captionDisplayMode() const 67{ 68 return m_displayMode; 69} 70 71void CaptionUserPreferences::setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode mode) 72{ 73 m_displayMode = mode; 74 if (m_testingMode && mode != AlwaysOn) { 75 setUserPrefersCaptions(false); 76 setUserPrefersSubtitles(false); 77 } 78 notify(); 79} 80 81bool CaptionUserPreferences::userPrefersCaptions() const 82{ 83 Page* page = *(m_pageGroup.pages().begin()); 84 if (!page) 85 return false; 86 87 return page->settings().shouldDisplayCaptions(); 88} 89 90void CaptionUserPreferences::setUserPrefersCaptions(bool preference) 91{ 92 Page* page = *(m_pageGroup.pages().begin()); 93 if (!page) 94 return; 95 96 page->settings().setShouldDisplayCaptions(preference); 97 notify(); 98} 99 100bool CaptionUserPreferences::userPrefersSubtitles() const 101{ 102 Page* page = *(pageGroup().pages().begin()); 103 if (!page) 104 return false; 105 106 return page->settings().shouldDisplaySubtitles(); 107} 108 109void CaptionUserPreferences::setUserPrefersSubtitles(bool preference) 110{ 111 Page* page = *(m_pageGroup.pages().begin()); 112 if (!page) 113 return; 114 115 page->settings().setShouldDisplaySubtitles(preference); 116 notify(); 117} 118 119bool CaptionUserPreferences::userPrefersTextDescriptions() const 120{ 121 Page* page = *(m_pageGroup.pages().begin()); 122 if (!page) 123 return false; 124 125 return page->settings().shouldDisplayTextDescriptions(); 126} 127 128void CaptionUserPreferences::setUserPrefersTextDescriptions(bool preference) 129{ 130 Page* page = *(m_pageGroup.pages().begin()); 131 if (!page) 132 return; 133 134 page->settings().setShouldDisplayTextDescriptions(preference); 135 notify(); 136} 137 138void CaptionUserPreferences::captionPreferencesChanged() 139{ 140 m_pageGroup.captionPreferencesChanged(); 141} 142 143Vector<String> CaptionUserPreferences::preferredLanguages() const 144{ 145 Vector<String> languages = userPreferredLanguages(); 146 if (m_testingMode && !m_userPreferredLanguage.isEmpty()) 147 languages.insert(0, m_userPreferredLanguage); 148 149 return languages; 150} 151 152void CaptionUserPreferences::setPreferredLanguage(const String& language) 153{ 154 m_userPreferredLanguage = language; 155 notify(); 156} 157 158static String trackDisplayName(TextTrack* track) 159{ 160 if (track == TextTrack::captionMenuOffItem()) 161 return textTrackOffMenuItemText(); 162 if (track == TextTrack::captionMenuAutomaticItem()) 163 return textTrackAutomaticMenuItemText(); 164 165 if (track->label().isEmpty() && track->language().isEmpty()) 166 return textTrackNoLabelText(); 167 if (!track->label().isEmpty()) 168 return track->label(); 169 return track->language(); 170} 171 172String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const 173{ 174 return trackDisplayName(track); 175} 176 177Vector<RefPtr<TextTrack>> CaptionUserPreferences::sortedTrackListForMenu(TextTrackList* trackList) 178{ 179 ASSERT(trackList); 180 181 Vector<RefPtr<TextTrack>> tracksForMenu; 182 183 for (unsigned i = 0, length = trackList->length(); i < length; ++i) { 184 TextTrack* track = trackList->item(i); 185 const AtomicString& kind = track->kind(); 186 if (kind == TextTrack::captionsKeyword() || kind == TextTrack::descriptionsKeyword() || kind == TextTrack::subtitlesKeyword()) 187 tracksForMenu.append(track); 188 } 189 190 std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b) { 191 return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0; 192 }); 193 194 tracksForMenu.insert(0, TextTrack::captionMenuOffItem()); 195 tracksForMenu.insert(1, TextTrack::captionMenuAutomaticItem()); 196 197 return tracksForMenu; 198} 199 200int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track, HTMLMediaElement*) const 201{ 202 int trackScore = 0; 203 204 if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword()) 205 return trackScore; 206 207 if (!userPrefersSubtitles() && !userPrefersCaptions()) 208 return trackScore; 209 210 if (track->kind() == TextTrack::subtitlesKeyword() && userPrefersSubtitles()) 211 trackScore = 1; 212 else if (track->kind() == TextTrack::captionsKeyword() && userPrefersCaptions()) 213 trackScore = 1; 214 215 return trackScore + textTrackLanguageSelectionScore(track, preferredLanguages()); 216} 217 218int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track, const Vector<String>& preferredLanguages) const 219{ 220 if (track->language().isEmpty()) 221 return 0; 222 223 size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), preferredLanguages); 224 if (languageMatchIndex >= preferredLanguages.size()) 225 return 0; 226 227 // Matching a track language is more important than matching track type, so this multiplier must be 228 // greater than the maximum value returned by textTrackSelectionScore. 229 return (preferredLanguages.size() - languageMatchIndex) * 10; 230} 231 232void CaptionUserPreferences::setCaptionsStyleSheetOverride(const String& override) 233{ 234 m_captionsStyleSheetOverride = override; 235 updateCaptionStyleSheetOveride(); 236} 237 238void CaptionUserPreferences::updateCaptionStyleSheetOveride() 239{ 240 // Identify our override style sheet with a unique URL - a new scheme and a UUID. 241 DEPRECATED_DEFINE_STATIC_LOCAL(URL, captionsStyleSheetURL, (ParsedURLString, "user-captions-override:01F6AF12-C3B0-4F70-AF5E-A3E00234DC23")); 242 243 auto& pages = m_pageGroup.pages(); 244 for (auto& page : pages) { 245 if (auto* pageUserContentController = page->userContentController()) 246 pageUserContentController->removeUserStyleSheet(mainThreadNormalWorld(), captionsStyleSheetURL); 247 } 248 249 String captionsOverrideStyleSheet = captionsStyleSheetOverride(); 250 if (captionsOverrideStyleSheet.isEmpty()) 251 return; 252 253 for (auto& page : pages) { 254 if (auto* pageUserContentController = page->userContentController()) { 255 auto userStyleSheet = std::make_unique<UserStyleSheet>(captionsOverrideStyleSheet, captionsStyleSheetURL, Vector<String>(), Vector<String>(), InjectInAllFrames, UserStyleAuthorLevel); 256 pageUserContentController->addUserStyleSheet(mainThreadNormalWorld(), std::move(userStyleSheet), InjectInExistingDocuments); 257 } 258 } 259} 260 261String CaptionUserPreferences::primaryAudioTrackLanguageOverride() const 262{ 263 if (!m_primaryAudioTrackLanguageOverride.isEmpty()) 264 return m_primaryAudioTrackLanguageOverride; 265 return defaultLanguage(); 266} 267 268} 269 270#endif // ENABLE(VIDEO_TRACK) 271