1/*
2 * Copyright (C) 2011-2017 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 "TrackBase.h"
28
29#include "Logging.h"
30#include <wtf/Language.h>
31#include <wtf/text/StringBuilder.h>
32
33#if ENABLE(VIDEO_TRACK)
34
35#include "HTMLMediaElement.h"
36
37namespace WebCore {
38
39static int s_uniqueId = 0;
40
41#if !RELEASE_LOG_DISABLED
42static const void* nextLogIdentifier()
43{
44 static uint64_t logIdentifier = cryptographicallyRandomNumber();
45 return reinterpret_cast<const void*>(++logIdentifier);
46}
47
48static RefPtr<Logger>& nullLogger()
49{
50 static NeverDestroyed<RefPtr<Logger>> logger;
51 return logger;
52}
53#endif
54
55TrackBase::TrackBase(Type type, const AtomicString& id, const AtomicString& label, const AtomicString& language)
56 : m_uniqueId(++s_uniqueId)
57 , m_id(id)
58 , m_label(label)
59 , m_language(language)
60 , m_validBCP47Language(language)
61{
62 ASSERT(type != BaseTrack);
63 m_type = type;
64
65#if !RELEASE_LOG_DISABLED
66 if (!nullLogger().get()) {
67 nullLogger() = Logger::create(this);
68 nullLogger()->setEnabled(this, false);
69 }
70
71 m_logger = nullLogger().get();
72 m_logIdentifier = nextLogIdentifier();
73#endif
74}
75
76Element* TrackBase::element()
77{
78 return m_mediaElement;
79}
80
81void TrackBase::setMediaElement(HTMLMediaElement* element)
82{
83 m_mediaElement = element;
84
85#if !RELEASE_LOG_DISABLED
86 if (element) {
87 m_logger = &element->logger();
88 m_logIdentifier = element->logIdentifier();
89 }
90#endif
91}
92
93// See: https://tools.ietf.org/html/bcp47#section-2.1
94static bool isValidBCP47LanguageTag(const String& languageTag)
95{
96 auto const length = languageTag.length();
97
98 // Max length picked as double the longest example tag in spec which is 49 characters:
99 // https://tools.ietf.org/html/bcp47#section-4.4.2
100 if (length < 2 || length > 100)
101 return false;
102
103 UChar firstChar = languageTag[0];
104
105 if (!isASCIIAlpha(firstChar))
106 return false;
107
108 UChar secondChar = languageTag[1];
109
110 if (length == 2)
111 return isASCIIAlpha(secondChar);
112
113 bool grandFatheredIrregularOrPrivateUse = (firstChar == 'i' || firstChar == 'x') && secondChar == '-';
114 unsigned nextCharIndexToCheck;
115
116 if (!grandFatheredIrregularOrPrivateUse) {
117 if (!isASCIIAlpha(secondChar))
118 return false;
119
120 if (length == 3)
121 return isASCIIAlpha(languageTag[2]);
122
123 if (isASCIIAlpha(languageTag[2])) {
124 if (languageTag[3] == '-')
125 nextCharIndexToCheck = 4;
126 else
127 return false;
128 } else if (languageTag[2] == '-')
129 nextCharIndexToCheck = 3;
130 else
131 return false;
132 } else
133 nextCharIndexToCheck = 2;
134
135 for (; nextCharIndexToCheck < length; ++nextCharIndexToCheck) {
136 UChar c = languageTag[nextCharIndexToCheck];
137 if (isASCIIAlphanumeric(c) || c == '-')
138 continue;
139 return false;
140 }
141 return true;
142}
143
144void TrackBase::setLanguage(const AtomicString& language)
145{
146 if (!language.isEmpty() && !isValidBCP47LanguageTag(language)) {
147 String message;
148 if (language.contains((UChar)'\0'))
149 message = "The language contains a null character and is not a valid BCP 47 language tag."_s;
150 else {
151 StringBuilder stringBuilder;
152 stringBuilder.appendLiteral("The language '");
153 stringBuilder.append(language);
154 stringBuilder.appendLiteral("' is not a valid BCP 47 language tag.");
155 message = stringBuilder.toString();
156 }
157 if (auto element = this->element())
158 element->document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Warning, message);
159 } else
160 m_validBCP47Language = language;
161
162 m_language = language;
163}
164
165AtomicString TrackBase::validBCP47Language() const
166{
167 return m_validBCP47Language;
168}
169
170#if !RELEASE_LOG_DISABLED
171WTFLogChannel& TrackBase::logChannel() const
172{
173 return LogMedia;
174}
175#endif
176
177MediaTrackBase::MediaTrackBase(Type type, const AtomicString& id, const AtomicString& label, const AtomicString& language)
178 : TrackBase(type, id, label, language)
179{
180}
181
182void MediaTrackBase::setKind(const AtomicString& kind)
183{
184 setKindInternal(kind);
185}
186
187void MediaTrackBase::setKindInternal(const AtomicString& kind)
188{
189 if (isValidKind(kind))
190 m_kind = kind;
191 else
192 m_kind = emptyAtom();
193}
194
195} // namespace WebCore
196
197#endif
198