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