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