1/*
2 * Copyright (C) 2011, 2013 Google Inc.  All rights reserved.
3 * Copyright (C) 2011, 2012, 2013, 2014 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
34#if ENABLE(VIDEO_TRACK)
35
36#include "TextTrackCue.h"
37
38#include "CSSPropertyNames.h"
39#include "CSSValueKeywords.h"
40#include "DocumentFragment.h"
41#include "Event.h"
42#include "HTMLDivElement.h"
43#include "HTMLSpanElement.h"
44#include "Logging.h"
45#include "NodeTraversal.h"
46#include "Text.h"
47#include "TextTrack.h"
48#include "TextTrackCueList.h"
49#include "VTTCue.h"
50#include <wtf/MathExtras.h>
51#include <wtf/text/StringBuilder.h>
52
53#if ENABLE(WEBVTT_REGIONS)
54#include "VTTRegionList.h"
55#endif
56
57namespace WebCore {
58
59static const int invalidCueIndex = -1;
60
61PassRefPtr<TextTrackCue> TextTrackCue::create(ScriptExecutionContext& context, double start, double end, const String& content)
62{
63    return VTTCue::create(context, start, end, content);
64}
65
66TextTrackCue::TextTrackCue(ScriptExecutionContext& context, double start, double end)
67    : m_startTime(start)
68    , m_endTime(end)
69    , m_cueIndex(invalidCueIndex)
70    , m_processingCueChanges(0)
71    , m_track(0)
72    , m_scriptExecutionContext(context)
73    , m_isActive(false)
74    , m_pauseOnExit(false)
75{
76    ASSERT(m_scriptExecutionContext.isDocument());
77}
78
79TextTrackCue::~TextTrackCue()
80{
81}
82
83void TextTrackCue::willChange()
84{
85    if (++m_processingCueChanges > 1)
86        return;
87
88    if (m_track)
89        m_track->cueWillChange(this);
90}
91
92void TextTrackCue::didChange()
93{
94    ASSERT(m_processingCueChanges);
95    if (--m_processingCueChanges)
96        return;
97
98    if (m_track)
99        m_track->cueDidChange(this);
100}
101
102TextTrack* TextTrackCue::track() const
103{
104    return m_track;
105}
106
107void TextTrackCue::setTrack(TextTrack* track)
108{
109    m_track = track;
110}
111
112void TextTrackCue::setId(const String& id)
113{
114    if (m_id == id)
115        return;
116
117    willChange();
118    m_id = id;
119    didChange();
120}
121
122void TextTrackCue::setStartTime(double value, ExceptionCode& ec)
123{
124    // NaN, Infinity and -Infinity values should trigger a TypeError.
125    if (std::isinf(value) || std::isnan(value)) {
126        ec = TypeError;
127        return;
128    }
129
130    // TODO(93143): Add spec-compliant behavior for negative time values.
131    if (m_startTime == value || value < 0)
132        return;
133
134    willChange();
135    m_startTime = value;
136    didChange();
137}
138
139void TextTrackCue::setEndTime(double value, ExceptionCode& ec)
140{
141    // NaN, Infinity and -Infinity values should trigger a TypeError.
142    if (std::isinf(value) || std::isnan(value)) {
143        ec = TypeError;
144        return;
145    }
146
147    // TODO(93143): Add spec-compliant behavior for negative time values.
148    if (m_endTime == value || value < 0)
149        return;
150
151    willChange();
152    m_endTime = value;
153    didChange();
154}
155
156void TextTrackCue::setPauseOnExit(bool value)
157{
158    if (m_pauseOnExit == value)
159        return;
160
161    m_pauseOnExit = value;
162}
163
164int TextTrackCue::cueIndex()
165{
166    if (m_cueIndex == invalidCueIndex) {
167        ASSERT(track());
168        ASSERT(track()->cues());
169        if (TextTrackCueList* cueList = track()->cues())
170            m_cueIndex = cueList->getCueIndex(this);
171    }
172
173    return m_cueIndex;
174}
175
176void TextTrackCue::invalidateCueIndex()
177{
178    m_cueIndex = invalidCueIndex;
179}
180
181bool TextTrackCue::dispatchEvent(PassRefPtr<Event> event)
182{
183    // When a TextTrack's mode is disabled: no cues are active, no events fired.
184    if (!track() || track()->mode() == TextTrack::disabledKeyword())
185        return false;
186
187    return EventTarget::dispatchEvent(event);
188}
189
190bool TextTrackCue::isActive()
191{
192    return m_isActive && track() && track()->mode() != TextTrack::disabledKeyword();
193}
194
195void TextTrackCue::setIsActive(bool active)
196{
197    m_isActive = active;
198}
199
200bool TextTrackCue::isOrderedBefore(const TextTrackCue* other) const
201{
202    return startTime() < other->startTime() || (startTime() == other->startTime() && endTime() > other->endTime());
203}
204
205bool TextTrackCue::cueContentsMatch(const TextTrackCue& cue) const
206{
207    if (cueType() != cue.cueType())
208        return false;
209
210    if (id() != cue.id())
211        return false;
212
213    return true;
214}
215
216bool TextTrackCue::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
217{
218    if (cueType() != cue.cueType())
219        return false;
220
221    if (match != IgnoreDuration && endTime() != cue.endTime())
222        return false;
223    if (!hasEquivalentStartTime(cue))
224        return false;
225    if (!cueContentsMatch(cue))
226        return false;
227
228    return true;
229}
230
231bool TextTrackCue::hasEquivalentStartTime(const TextTrackCue& cue) const
232{
233    double startTimeVariance = 0;
234    if (track())
235        startTimeVariance = track()->startTimeVariance();
236    else if (cue.track())
237        startTimeVariance = cue.track()->startTimeVariance();
238
239    return std::abs(std::abs(startTime()) - std::abs(cue.startTime())) <= startTimeVariance;
240}
241
242bool TextTrackCue::doesExtendCue(const TextTrackCue& cue) const
243{
244    if (!cueContentsMatch(cue))
245        return false;
246
247    if (endTime() != cue.startTime())
248        return false;
249
250    return true;
251}
252
253} // namespace WebCore
254
255#endif
256