Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / track / TextTrack.cpp
1 /*
2  * Copyright (C) 2011 Google Inc.  All rights reserved.
3  * Copyright (C) 2011, 2012, 2013 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 "core/html/track/TextTrack.h"
34
35 #include "RuntimeEnabledFeatures.h"
36 #include "bindings/v8/ExceptionState.h"
37 #include "bindings/v8/ExceptionStatePlaceholder.h"
38 #include "core/dom/Document.h"
39 #include "core/dom/ExceptionCode.h"
40 #include "core/html/HTMLMediaElement.h"
41 #include "core/html/track/TextTrackCueList.h"
42 #include "core/html/track/TextTrackList.h"
43 #include "core/html/track/vtt/VTTRegion.h"
44 #include "core/html/track/vtt/VTTRegionList.h"
45
46 namespace WebCore {
47
48 static const int invalidTrackIndex = -1;
49
50 const AtomicString& TextTrack::subtitlesKeyword()
51 {
52     DEFINE_STATIC_LOCAL(const AtomicString, subtitles, ("subtitles", AtomicString::ConstructFromLiteral));
53     return subtitles;
54 }
55
56 const AtomicString& TextTrack::captionsKeyword()
57 {
58     DEFINE_STATIC_LOCAL(const AtomicString, captions, ("captions", AtomicString::ConstructFromLiteral));
59     return captions;
60 }
61
62 const AtomicString& TextTrack::descriptionsKeyword()
63 {
64     DEFINE_STATIC_LOCAL(const AtomicString, descriptions, ("descriptions", AtomicString::ConstructFromLiteral));
65     return descriptions;
66 }
67
68 const AtomicString& TextTrack::chaptersKeyword()
69 {
70     DEFINE_STATIC_LOCAL(const AtomicString, chapters, ("chapters", AtomicString::ConstructFromLiteral));
71     return chapters;
72 }
73
74 const AtomicString& TextTrack::metadataKeyword()
75 {
76     DEFINE_STATIC_LOCAL(const AtomicString, metadata, ("metadata", AtomicString::ConstructFromLiteral));
77     return metadata;
78 }
79
80 const AtomicString& TextTrack::disabledKeyword()
81 {
82     DEFINE_STATIC_LOCAL(const AtomicString, open, ("disabled", AtomicString::ConstructFromLiteral));
83     return open;
84 }
85
86 const AtomicString& TextTrack::hiddenKeyword()
87 {
88     DEFINE_STATIC_LOCAL(const AtomicString, closed, ("hidden", AtomicString::ConstructFromLiteral));
89     return closed;
90 }
91
92 const AtomicString& TextTrack::showingKeyword()
93 {
94     DEFINE_STATIC_LOCAL(const AtomicString, ended, ("showing", AtomicString::ConstructFromLiteral));
95     return ended;
96 }
97
98 TextTrack::TextTrack(Document& document, TextTrackClient* client, const AtomicString& kind, const AtomicString& label, const AtomicString& language, const AtomicString& id, TextTrackType type)
99     : TrackBase(TrackBase::TextTrack, label, language, id)
100     , m_cues(0)
101     , m_regions(0)
102     , m_document(&document)
103     , m_trackList(0)
104     , m_mode(disabledKeyword())
105     , m_client(client)
106     , m_trackType(type)
107     , m_readinessState(NotLoaded)
108     , m_trackIndex(invalidTrackIndex)
109     , m_renderedTrackIndex(invalidTrackIndex)
110     , m_hasBeenConfigured(false)
111 {
112     ScriptWrappable::init(this);
113     setKind(kind);
114 }
115
116 TextTrack::~TextTrack()
117 {
118     if (m_cues) {
119         if (m_client)
120             m_client->textTrackRemoveCues(this, m_cues.get());
121
122         for (size_t i = 0; i < m_cues->length(); ++i)
123             m_cues->item(i)->setTrack(0);
124     }
125
126     if (m_regions) {
127         for (size_t i = 0; i < m_regions->length(); ++i)
128             m_regions->item(i)->setTrack(0);
129     }
130     clearClient();
131 }
132
133 bool TextTrack::isValidKindKeyword(const AtomicString& value)
134 {
135     if (value == subtitlesKeyword())
136         return true;
137     if (value == captionsKeyword())
138         return true;
139     if (value == descriptionsKeyword())
140         return true;
141     if (value == chaptersKeyword())
142         return true;
143     if (value == metadataKeyword())
144         return true;
145
146     return false;
147 }
148
149 void TextTrack::setKind(const AtomicString& newKind)
150 {
151     AtomicString oldKind = kind();
152     TrackBase::setKind(newKind);
153
154     if (m_client && oldKind != kind())
155         m_client->textTrackKindChanged(this);
156 }
157
158 void TextTrack::setMode(const AtomicString& mode)
159 {
160     ASSERT(mode == disabledKeyword() || mode == hiddenKeyword() || mode == showingKeyword());
161
162     // On setting, if the new value isn't equal to what the attribute would currently
163     // return, the new value must be processed as follows ...
164     if (m_mode == mode)
165         return;
166
167     // If mode changes to disabled, remove this track's cues from the client
168     // because they will no longer be accessible from the cues() function.
169     if (mode == disabledKeyword() && m_client && m_cues)
170         m_client->textTrackRemoveCues(this, m_cues.get());
171
172     if (mode != showingKeyword() && m_cues)
173         for (size_t i = 0; i < m_cues->length(); ++i)
174             m_cues->item(i)->removeDisplayTree();
175
176     m_mode = mode;
177
178     if (m_client)
179         m_client->textTrackModeChanged(this);
180 }
181
182 TextTrackCueList* TextTrack::cues()
183 {
184     // 4.8.10.12.5 If the text track mode ... is not the text track disabled mode,
185     // then the cues attribute must return a live TextTrackCueList object ...
186     // Otherwise, it must return null. When an object is returned, the
187     // same object must be returned each time.
188     // http://www.whatwg.org/specs/web-apps/current-work/#dom-texttrack-cues
189     if (m_mode != disabledKeyword())
190         return ensureTextTrackCueList();
191     return 0;
192 }
193
194 void TextTrack::removeAllCues()
195 {
196     if (!m_cues)
197         return;
198
199     if (m_client)
200         m_client->textTrackRemoveCues(this, m_cues.get());
201
202     for (size_t i = 0; i < m_cues->length(); ++i)
203         m_cues->item(i)->setTrack(0);
204
205     m_cues = 0;
206 }
207
208 TextTrackCueList* TextTrack::activeCues() const
209 {
210     // 4.8.10.12.5 If the text track mode ... is not the text track disabled mode,
211     // then the activeCues attribute must return a live TextTrackCueList object ...
212     // ... whose active flag was set when the script started, in text track cue
213     // order. Otherwise, it must return null. When an object is returned, the
214     // same object must be returned each time.
215     // http://www.whatwg.org/specs/web-apps/current-work/#dom-texttrack-activecues
216     if (m_cues && m_mode != disabledKeyword())
217         return m_cues->activeCues();
218     return 0;
219 }
220
221 void TextTrack::addCue(PassRefPtr<TextTrackCue> prpCue)
222 {
223     if (!prpCue)
224         return;
225
226     RefPtr<TextTrackCue> cue = prpCue;
227
228     // TODO(93143): Add spec-compliant behavior for negative time values.
229     if (std::isnan(cue->startTime()) || std::isnan(cue->endTime()) || cue->startTime() < 0 || cue->endTime() < 0)
230         return;
231
232     // 4.8.10.12.5 Text track API
233
234     // The addCue(cue) method of TextTrack objects, when invoked, must run the following steps:
235
236     // 1. If the given cue is in a text track list of cues, then remove cue from that text track
237     // list of cues.
238     TextTrack* cueTrack = cue->track();
239     if (cueTrack && cueTrack != this)
240         cueTrack->removeCue(cue.get(), ASSERT_NO_EXCEPTION);
241
242     // 2. Add cue to the method's TextTrack object's text track's text track list of cues.
243     cue->setTrack(this);
244     ensureTextTrackCueList()->add(cue);
245
246     if (m_client)
247         m_client->textTrackAddCue(this, cue.get());
248 }
249
250 void TextTrack::removeCue(TextTrackCue* cue, ExceptionState& exceptionState)
251 {
252     if (!cue)
253         return;
254
255     // 4.8.10.12.5 Text track API
256
257     // The removeCue(cue) method of TextTrack objects, when invoked, must run the following steps:
258
259     // 1. If the given cue is not currently listed in the method's TextTrack
260     // object's text track's text track list of cues, then throw a NotFoundError exception.
261     if (cue->track() != this) {
262         exceptionState.throwDOMException(NotFoundError, "The specified cue is not listed in the TextTrack's list of cues.");
263         return;
264     }
265
266     // 2. Remove cue from the method's TextTrack object's text track's text track list of cues.
267     if (!m_cues || !m_cues->remove(cue)) {
268         exceptionState.throwDOMException(InvalidStateError, "Failed to remove the specified cue.");
269         return;
270     }
271
272     cue->setTrack(0);
273     if (m_client)
274         m_client->textTrackRemoveCue(this, cue);
275 }
276
277 VTTRegionList* TextTrack::ensureVTTRegionList()
278 {
279     if (!m_regions)
280         m_regions = VTTRegionList::create();
281
282     return m_regions.get();
283 }
284
285 VTTRegionList* TextTrack::regions()
286 {
287     // If the text track mode of the text track that the TextTrack object
288     // represents is not the text track disabled mode, then the regions
289     // attribute must return a live VTTRegionList object that represents
290     // the text track list of regions of the text track. Otherwise, it must
291     // return null. When an object is returned, the same object must be returned
292     // each time.
293     if (RuntimeEnabledFeatures::webVTTRegionsEnabled() && m_mode != disabledKeyword())
294         return ensureVTTRegionList();
295     return 0;
296 }
297
298 void TextTrack::addRegion(PassRefPtr<VTTRegion> prpRegion)
299 {
300     if (!prpRegion)
301         return;
302
303     RefPtr<VTTRegion> region = prpRegion;
304     VTTRegionList* regionList = ensureVTTRegionList();
305
306     // 1. If the given region is in a text track list of regions, then remove
307     // region from that text track list of regions.
308     TextTrack* regionTrack = region->track();
309     if (regionTrack && regionTrack != this)
310         regionTrack->removeRegion(region.get(), ASSERT_NO_EXCEPTION);
311
312     // 2. If the method's TextTrack object's text track list of regions contains
313     // a region with the same identifier as region replace the values of that
314     // region's width, height, anchor point, viewport anchor point and scroll
315     // attributes with those of region.
316     VTTRegion* existingRegion = regionList->getRegionById(region->id());
317     if (existingRegion) {
318         existingRegion->updateParametersFromRegion(region.get());
319         return;
320     }
321
322     // Otherwise: add region to the method's TextTrack object's text track
323     // list of regions.
324     region->setTrack(this);
325     regionList->add(region);
326 }
327
328 void TextTrack::removeRegion(VTTRegion* region, ExceptionState &exceptionState)
329 {
330     if (!region)
331         return;
332
333     // 1. If the given region is not currently listed in the method's TextTrack
334     // object's text track list of regions, then throw a NotFoundError exception.
335     if (region->track() != this) {
336         exceptionState.throwDOMException(NotFoundError, "The specified region is not listed in the TextTrack's list of regions.");
337         return;
338     }
339
340     if (!m_regions || !m_regions->remove(region)) {
341         exceptionState.throwDOMException(InvalidStateError, "Failed to remove the specified region.");
342         return;
343     }
344
345     region->setTrack(0);
346 }
347
348 void TextTrack::cueWillChange(TextTrackCue* cue)
349 {
350     if (!m_client)
351         return;
352
353     // The cue may need to be repositioned in the media element's interval tree, may need to
354     // be re-rendered, etc, so remove it before the modification...
355     m_client->textTrackRemoveCue(this, cue);
356 }
357
358 void TextTrack::cueDidChange(TextTrackCue* cue)
359 {
360     if (!m_client)
361         return;
362
363     // Make sure the TextTrackCueList order is up-to-date.
364     ensureTextTrackCueList()->updateCueIndex(cue);
365
366     // ... and add it back again.
367     m_client->textTrackAddCue(this, cue);
368 }
369
370 int TextTrack::trackIndex()
371 {
372     ASSERT(m_trackList);
373
374     if (m_trackIndex == invalidTrackIndex)
375         m_trackIndex = m_trackList->getTrackIndex(this);
376
377     return m_trackIndex;
378 }
379
380 void TextTrack::invalidateTrackIndex()
381 {
382     m_trackIndex = invalidTrackIndex;
383     m_renderedTrackIndex = invalidTrackIndex;
384 }
385
386 bool TextTrack::isRendered()
387 {
388     if (kind() != captionsKeyword() && kind() != subtitlesKeyword())
389         return false;
390
391     if (m_mode != showingKeyword())
392         return false;
393
394     return true;
395 }
396
397 TextTrackCueList* TextTrack::ensureTextTrackCueList()
398 {
399     if (!m_cues)
400         m_cues = TextTrackCueList::create();
401
402     return m_cues.get();
403 }
404
405 int TextTrack::trackIndexRelativeToRenderedTracks()
406 {
407     ASSERT(m_trackList);
408
409     if (m_renderedTrackIndex == invalidTrackIndex)
410         m_renderedTrackIndex = m_trackList->getTrackIndexRelativeToRenderedTracks(this);
411
412     return m_renderedTrackIndex;
413 }
414
415 const AtomicString& TextTrack::interfaceName() const
416 {
417     return EventTargetNames::TextTrack;
418 }
419
420 ExecutionContext* TextTrack::executionContext() const
421 {
422     return m_document;
423 }
424
425 } // namespace WebCore
426