Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / page / CaptionUserPreferences.cpp
1 /*
2  * Copyright (C) 2013, 2015 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 #include "CaptionUserPreferences.h"
28
29 #if ENABLE(VIDEO_TRACK)
30
31 #include "AudioTrackList.h"
32 #include "DOMWrapperWorld.h"
33 #include "Page.h"
34 #include "PageGroup.h"
35 #include "Settings.h"
36 #include "TextTrackList.h"
37 #include "UserContentController.h"
38 #include "UserContentTypes.h"
39 #include "UserStyleSheet.h"
40 #include "UserStyleSheetTypes.h"
41 #include <runtime/JSCellInlines.h>
42 #include <runtime/StructureInlines.h>
43
44 namespace WebCore {
45
46 CaptionUserPreferences::CaptionUserPreferences(PageGroup& group)
47     : m_pageGroup(group)
48     , m_displayMode(ForcedOnly)
49     , m_timer(*this, &CaptionUserPreferences::timerFired)
50     , m_testingMode(false)
51     , m_havePreferences(false)
52 {
53 }
54
55 CaptionUserPreferences::~CaptionUserPreferences()
56 {
57 }
58
59 void CaptionUserPreferences::timerFired()
60 {
61     captionPreferencesChanged();
62 }
63
64 void CaptionUserPreferences::notify()
65 {
66     m_havePreferences = true;
67     if (!m_timer.isActive())
68         m_timer.startOneShot(0);
69 }
70
71 CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferences::captionDisplayMode() const
72 {
73     return m_displayMode;
74 }
75
76 void CaptionUserPreferences::setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode mode)
77 {
78     m_displayMode = mode;
79     if (m_testingMode && mode != AlwaysOn) {
80         setUserPrefersCaptions(false);
81         setUserPrefersSubtitles(false);
82     }
83     notify();
84 }
85
86 bool CaptionUserPreferences::userPrefersCaptions() const
87 {
88     Page* page = *(m_pageGroup.pages().begin());
89     if (!page)
90         return false;
91
92     return page->settings().shouldDisplayCaptions();
93 }
94
95 void CaptionUserPreferences::setUserPrefersCaptions(bool preference)
96 {
97     Page* page = *(m_pageGroup.pages().begin());
98     if (!page)
99         return;
100
101     page->settings().setShouldDisplayCaptions(preference);
102     notify();
103 }
104
105 bool CaptionUserPreferences::userPrefersSubtitles() const
106 {
107     Page* page = *(pageGroup().pages().begin());
108     if (!page)
109         return false;
110
111     return page->settings().shouldDisplaySubtitles();
112 }
113
114 void CaptionUserPreferences::setUserPrefersSubtitles(bool preference)
115 {
116     Page* page = *(m_pageGroup.pages().begin());
117     if (!page)
118         return;
119
120     page->settings().setShouldDisplaySubtitles(preference);
121     notify();
122 }
123
124 bool CaptionUserPreferences::userPrefersTextDescriptions() const
125 {
126     Page* page = *(m_pageGroup.pages().begin());
127     if (!page)
128         return false;
129     
130     return page->settings().shouldDisplayTextDescriptions();
131 }
132
133 void CaptionUserPreferences::setUserPrefersTextDescriptions(bool preference)
134 {
135     Page* page = *(m_pageGroup.pages().begin());
136     if (!page)
137         return;
138     
139     page->settings().setShouldDisplayTextDescriptions(preference);
140     notify();
141 }
142
143 void CaptionUserPreferences::captionPreferencesChanged()
144 {
145     m_pageGroup.captionPreferencesChanged();
146 }
147
148 Vector<String> CaptionUserPreferences::preferredLanguages() const
149 {
150     Vector<String> languages = userPreferredLanguages();
151     if (m_testingMode && !m_userPreferredLanguage.isEmpty())
152         languages.insert(0, m_userPreferredLanguage);
153
154     return languages;
155 }
156
157 void CaptionUserPreferences::setPreferredLanguage(const String& language)
158 {
159     m_userPreferredLanguage = language;
160     notify();
161 }
162
163 void CaptionUserPreferences::setPreferredAudioCharacteristic(const String& characteristic)
164 {
165     m_userPreferredAudioCharacteristic = characteristic;
166     notify();
167 }
168
169 Vector<String> CaptionUserPreferences::preferredAudioCharacteristics() const
170 {
171     Vector<String> characteristics;
172     if (!m_userPreferredAudioCharacteristic.isEmpty())
173         characteristics.append(m_userPreferredAudioCharacteristic);
174     return characteristics;
175 }
176
177 static String trackDisplayName(TextTrack* track)
178 {
179     if (track == TextTrack::captionMenuOffItem())
180         return textTrackOffMenuItemText();
181     if (track == TextTrack::captionMenuAutomaticItem())
182         return textTrackAutomaticMenuItemText();
183
184     if (track->label().isEmpty() && track->language().isEmpty())
185         return textTrackNoLabelText();
186     if (!track->label().isEmpty())
187         return track->label();
188     return track->language();
189 }
190
191 String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const
192 {
193     return trackDisplayName(track);
194 }
195     
196 Vector<RefPtr<TextTrack>> CaptionUserPreferences::sortedTrackListForMenu(TextTrackList* trackList)
197 {
198     ASSERT(trackList);
199
200     Vector<RefPtr<TextTrack>> tracksForMenu;
201
202     for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
203         TextTrack* track = trackList->item(i);
204         const AtomicString& kind = track->kind();
205         if (kind == TextTrack::captionsKeyword() || kind == TextTrack::descriptionsKeyword() || kind == TextTrack::subtitlesKeyword())
206             tracksForMenu.append(track);
207     }
208
209     std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b) {
210         return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
211     });
212
213     tracksForMenu.insert(0, TextTrack::captionMenuOffItem());
214     tracksForMenu.insert(1, TextTrack::captionMenuAutomaticItem());
215
216     return tracksForMenu;
217 }
218
219 static String trackDisplayName(AudioTrack* track)
220 {
221     if (track->label().isEmpty() && track->language().isEmpty())
222         return audioTrackNoLabelText();
223     if (!track->label().isEmpty())
224         return track->label();
225     return track->language();
226 }
227
228 String CaptionUserPreferences::displayNameForTrack(AudioTrack* track) const
229 {
230     return trackDisplayName(track);
231 }
232
233 Vector<RefPtr<AudioTrack>> CaptionUserPreferences::sortedTrackListForMenu(AudioTrackList* trackList)
234 {
235     ASSERT(trackList);
236
237     Vector<RefPtr<AudioTrack>> tracksForMenu;
238
239     for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
240         AudioTrack* track = trackList->item(i);
241         tracksForMenu.append(track);
242     }
243
244     std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](const RefPtr<AudioTrack>& a, const RefPtr<AudioTrack>& b) {
245         return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
246     });
247
248     return tracksForMenu;
249 }
250
251 int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track, HTMLMediaElement*) const
252 {
253     if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
254         return 0;
255     
256     if (!userPrefersSubtitles() && !userPrefersCaptions())
257         return 0;
258     
259     return textTrackLanguageSelectionScore(track, preferredLanguages()) + 1;
260 }
261
262 int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track, const Vector<String>& preferredLanguages) const
263 {
264     if (track->language().isEmpty())
265         return 0;
266
267     bool exactMatch;
268     size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), preferredLanguages, exactMatch);
269     if (languageMatchIndex >= preferredLanguages.size())
270         return 0;
271
272     // Matching a track language is more important than matching track type, so this multiplier must be
273     // greater than the maximum value returned by textTrackSelectionScore.
274     int bonus = exactMatch ? 1 : 0;
275     return (preferredLanguages.size() + bonus - languageMatchIndex) * 10;
276 }
277
278 void CaptionUserPreferences::setCaptionsStyleSheetOverride(const String& override)
279 {
280     m_captionsStyleSheetOverride = override;
281     updateCaptionStyleSheetOveride();
282 }
283
284 void CaptionUserPreferences::updateCaptionStyleSheetOveride()
285 {
286     // Identify our override style sheet with a unique URL - a new scheme and a UUID.
287     DEPRECATED_DEFINE_STATIC_LOCAL(URL, captionsStyleSheetURL, (ParsedURLString, "user-captions-override:01F6AF12-C3B0-4F70-AF5E-A3E00234DC23"));
288
289     auto& pages = m_pageGroup.pages();
290     for (auto& page : pages) {
291         if (auto* pageUserContentController = page->userContentController())
292             pageUserContentController->removeUserStyleSheet(mainThreadNormalWorld(), captionsStyleSheetURL);
293     }
294
295     String captionsOverrideStyleSheet = captionsStyleSheetOverride();
296     if (captionsOverrideStyleSheet.isEmpty())
297         return;
298
299     for (auto& page : pages) {
300         if (auto* pageUserContentController = page->userContentController()) {
301             auto userStyleSheet = std::make_unique<UserStyleSheet>(captionsOverrideStyleSheet, captionsStyleSheetURL, Vector<String>(), Vector<String>(), InjectInAllFrames, UserStyleAuthorLevel);
302             pageUserContentController->addUserStyleSheet(mainThreadNormalWorld(), WTFMove(userStyleSheet), InjectInExistingDocuments);
303         }
304     }
305 }
306
307 String CaptionUserPreferences::primaryAudioTrackLanguageOverride() const
308 {
309     if (!m_primaryAudioTrackLanguageOverride.isEmpty())
310         return m_primaryAudioTrackLanguageOverride;
311     return defaultLanguage();
312 }
313     
314 }
315
316 #endif // ENABLE(VIDEO_TRACK)