1/*
2 * Copyright (C) 2011, 2013 Google Inc. All rights reserved.
3 * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "TextTrackCue.h"
34
35#if ENABLE(VIDEO_TRACK)
36
37#include "CSSPropertyNames.h"
38#include "CSSValueKeywords.h"
39#include "Event.h"
40#include "Logging.h"
41#include "NodeTraversal.h"
42#include "Text.h"
43#include "TextTrack.h"
44#include "TextTrackCueList.h"
45#include "VTTCue.h"
46#include "VTTRegionList.h"
47#include <wtf/HexNumber.h>
48#include <wtf/IsoMallocInlines.h>
49#include <wtf/MathExtras.h>
50#include <wtf/NeverDestroyed.h>
51#include <wtf/text/StringConcatenateNumbers.h>
52
53namespace WebCore {
54
55WTF_MAKE_ISO_ALLOCATED_IMPL(TextTrackCue);
56
57const AtomicString& TextTrackCue::cueShadowPseudoId()
58{
59 static NeverDestroyed<const AtomicString> cue("cue", AtomicString::ConstructFromLiteral);
60 return cue;
61}
62
63TextTrackCue::TextTrackCue(ScriptExecutionContext& context, const MediaTime& start, const MediaTime& end)
64 : m_startTime(start)
65 , m_endTime(end)
66 , m_scriptExecutionContext(context)
67 , m_isActive(false)
68 , m_pauseOnExit(false)
69{
70 ASSERT(m_scriptExecutionContext.isDocument());
71}
72
73void TextTrackCue::willChange()
74{
75 if (++m_processingCueChanges > 1)
76 return;
77
78 if (m_track)
79 m_track->cueWillChange(this);
80}
81
82void TextTrackCue::didChange()
83{
84 ASSERT(m_processingCueChanges);
85 if (--m_processingCueChanges)
86 return;
87
88 if (m_track)
89 m_track->cueDidChange(this);
90}
91
92TextTrack* TextTrackCue::track() const
93{
94 return m_track;
95}
96
97void TextTrackCue::setTrack(TextTrack* track)
98{
99 m_track = track;
100}
101
102void TextTrackCue::setId(const String& id)
103{
104 if (m_id == id)
105 return;
106
107 willChange();
108 m_id = id;
109 didChange();
110}
111
112void TextTrackCue::setStartTime(double value)
113{
114 // TODO(93143): Add spec-compliant behavior for negative time values.
115 if (m_startTime.toDouble() == value || value < 0)
116 return;
117
118 setStartTime(MediaTime::createWithDouble(value));
119}
120
121void TextTrackCue::setStartTime(const MediaTime& value)
122{
123 willChange();
124 m_startTime = value;
125 didChange();
126}
127
128void TextTrackCue::setEndTime(double value)
129{
130 // TODO(93143): Add spec-compliant behavior for negative time values.
131 if (m_endTime.toDouble() == value || value < 0)
132 return;
133
134 setEndTime(MediaTime::createWithDouble(value));
135}
136
137void TextTrackCue::setEndTime(const MediaTime& value)
138{
139 willChange();
140 m_endTime = value;
141 didChange();
142}
143
144void TextTrackCue::setPauseOnExit(bool value)
145{
146 if (m_pauseOnExit == value)
147 return;
148
149 m_pauseOnExit = value;
150}
151
152void TextTrackCue::dispatchEvent(Event& event)
153{
154 // When a TextTrack's mode is disabled: no cues are active, no events fired.
155 if (!track() || track()->mode() == TextTrack::Mode::Disabled)
156 return;
157
158 EventTarget::dispatchEvent(event);
159}
160
161bool TextTrackCue::isActive()
162{
163 return m_isActive && track() && track()->mode() != TextTrack::Mode::Disabled;
164}
165
166void TextTrackCue::setIsActive(bool active)
167{
168 m_isActive = active;
169}
170
171bool TextTrackCue::isOrderedBefore(const TextTrackCue* other) const
172{
173 return startMediaTime() < other->startMediaTime() || (startMediaTime() == other->startMediaTime() && endMediaTime() > other->endMediaTime());
174}
175
176bool TextTrackCue::cueContentsMatch(const TextTrackCue& cue) const
177{
178 if (cueType() != cue.cueType())
179 return false;
180
181 if (id() != cue.id())
182 return false;
183
184 return true;
185}
186
187bool TextTrackCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
188{
189 if (cueType() != cue.cueType())
190 return false;
191
192 if (match != IgnoreDuration && endMediaTime() != cue.endMediaTime())
193 return false;
194 if (!hasEquivalentStartTime(cue))
195 return false;
196 if (!cueContentsMatch(cue))
197 return false;
198
199 return true;
200}
201
202bool TextTrackCue::hasEquivalentStartTime(const TextTrackCue& cue) const
203{
204 MediaTime startTimeVariance = MediaTime::zeroTime();
205 if (track())
206 startTimeVariance = track()->startTimeVariance();
207 else if (cue.track())
208 startTimeVariance = cue.track()->startTimeVariance();
209
210 return abs(abs(startMediaTime()) - abs(cue.startMediaTime())) <= startTimeVariance;
211}
212
213bool TextTrackCue::doesExtendCue(const TextTrackCue& cue) const
214{
215 if (!cueContentsMatch(cue))
216 return false;
217
218 if (endMediaTime() != cue.startMediaTime())
219 return false;
220
221 return true;
222}
223
224void TextTrackCue::toJSON(JSON::Object& value) const
225{
226 ASCIILiteral type = "Generic"_s;
227 switch (cueType()) {
228 case TextTrackCue::Generic:
229 type = "Generic"_s;
230 break;
231 case TextTrackCue::WebVTT:
232 type = "WebVTT"_s;
233 break;
234 case TextTrackCue::Data:
235 type = "Data"_s;
236 break;
237 }
238
239 value.setString("type"_s, type);
240 value.setDouble("startTime"_s, startTime());
241 value.setDouble("endTime"_s, endTime());
242}
243
244String TextTrackCue::toJSONString() const
245{
246 auto object = JSON::Object::create();
247
248 toJSON(object.get());
249
250 return object->toJSONString();
251}
252
253String TextTrackCue::debugString() const
254{
255 String text;
256 if (isRenderable())
257 text = toVTTCue(this)->text();
258 return makeString("0x", hex(reinterpret_cast<uintptr_t>(this)), " id=", id(), " interval=", startTime(), "-->", endTime(), " cue=", text, ')');
259}
260
261} // namespace WebCore
262
263#endif
264