Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLTrackElement.cpp
1 /*
2  * Copyright (C) 2011 Google 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 COMPUTER, 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 COMPUTER, 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 "core/html/HTMLTrackElement.h"
28
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "core/HTMLNames.h"
31 #include "core/dom/Document.h"
32 #include "core/events/Event.h"
33 #include "core/frame/csp/ContentSecurityPolicy.h"
34 #include "core/html/HTMLMediaElement.h"
35 #include "platform/Logging.h"
36
37 namespace blink {
38
39 using namespace HTMLNames;
40
41 #if !LOG_DISABLED
42 static String urlForLoggingTrack(const KURL& url)
43 {
44     static const unsigned maximumURLLengthForLogging = 128;
45
46     if (url.string().length() < maximumURLLengthForLogging)
47         return url.string();
48     return url.string().substring(0, maximumURLLengthForLogging) + "...";
49 }
50 #endif
51
52 inline HTMLTrackElement::HTMLTrackElement(Document& document)
53     : HTMLElement(trackTag, document)
54     , m_loadTimer(this, &HTMLTrackElement::loadTimerFired)
55 {
56     WTF_LOG(Media, "HTMLTrackElement::HTMLTrackElement - %p", this);
57 }
58
59 DEFINE_NODE_FACTORY(HTMLTrackElement)
60
61 HTMLTrackElement::~HTMLTrackElement()
62 {
63 #if !ENABLE(OILPAN)
64     if (m_track)
65         m_track->clearTrackElement();
66 #endif
67 }
68
69 Node::InsertionNotificationRequest HTMLTrackElement::insertedInto(ContainerNode* insertionPoint)
70 {
71     WTF_LOG(Media, "HTMLTrackElement::insertedInto");
72
73     // Since we've moved to a new parent, we may now be able to load.
74     scheduleLoad();
75
76     HTMLElement::insertedInto(insertionPoint);
77     HTMLMediaElement* parent = mediaElement();
78     if (insertionPoint == parent)
79         parent->didAddTrackElement(this);
80     return InsertionDone;
81 }
82
83 void HTMLTrackElement::removedFrom(ContainerNode* insertionPoint)
84 {
85     if (!parentNode() && isHTMLMediaElement(*insertionPoint))
86         toHTMLMediaElement(insertionPoint)->didRemoveTrackElement(this);
87     HTMLElement::removedFrom(insertionPoint);
88 }
89
90 void HTMLTrackElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
91 {
92     if (name == srcAttr) {
93         if (!value.isEmpty())
94             scheduleLoad();
95         else if (m_track)
96             m_track->removeAllCues();
97
98     // 4.8.10.12.3 Sourcing out-of-band text tracks
99     // As the kind, label, and srclang attributes are set, changed, or removed, the text track must update accordingly...
100     } else if (name == kindAttr) {
101         track()->setKind(value.lower());
102     } else if (name == labelAttr) {
103         track()->setLabel(value);
104     } else if (name == srclangAttr) {
105         track()->setLanguage(value);
106     } else if (name == idAttr) {
107         track()->setId(value);
108     } else if (name == defaultAttr) {
109         track()->setIsDefault(!value.isNull());
110     }
111
112     HTMLElement::parseAttribute(name, value);
113 }
114
115 const AtomicString& HTMLTrackElement::kind()
116 {
117     return track()->kind();
118 }
119
120 void HTMLTrackElement::setKind(const AtomicString& kind)
121 {
122     setAttribute(kindAttr, kind);
123 }
124
125 LoadableTextTrack* HTMLTrackElement::ensureTrack()
126 {
127     if (!m_track) {
128         // kind, label and language are updated by parseAttribute
129         m_track = LoadableTextTrack::create(this);
130     }
131     return m_track.get();
132 }
133
134 TextTrack* HTMLTrackElement::track()
135 {
136     return ensureTrack();
137 }
138
139 bool HTMLTrackElement::isURLAttribute(const Attribute& attribute) const
140 {
141     return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
142 }
143
144 void HTMLTrackElement::scheduleLoad()
145 {
146     WTF_LOG(Media, "HTMLTrackElement::scheduleLoad");
147
148     // 1. If another occurrence of this algorithm is already running for this text track and its track element,
149     // abort these steps, letting that other algorithm take care of this element.
150     if (m_loadTimer.isActive())
151         return;
152
153     // 2. If the text track's text track mode is not set to one of hidden or showing, abort these steps.
154     if (ensureTrack()->mode() != TextTrack::hiddenKeyword() && ensureTrack()->mode() != TextTrack::showingKeyword())
155         return;
156
157     // 3. If the text track's track element does not have a media element as a parent, abort these steps.
158     if (!mediaElement())
159         return;
160
161     // 4. Run the remainder of these steps asynchronously, allowing whatever caused these steps to run to continue.
162     m_loadTimer.startOneShot(0, FROM_HERE);
163 }
164
165 void HTMLTrackElement::loadTimerFired(Timer<HTMLTrackElement>*)
166 {
167     if (!fastHasAttribute(srcAttr))
168         return;
169
170     WTF_LOG(Media, "HTMLTrackElement::loadTimerFired");
171
172     // 6. Set the text track readiness state to loading.
173     setReadyState(HTMLTrackElement::LOADING);
174
175     // 7. Let URL be the track URL of the track element.
176     KURL url = getNonEmptyURLAttribute(srcAttr);
177
178     // 8. If the track element's parent is a media element then let CORS mode be the state of the parent media
179     // element's crossorigin content attribute. Otherwise, let CORS mode be No CORS.
180     if (!canLoadUrl(url)) {
181         didCompleteLoad(HTMLTrackElement::Failure);
182         return;
183     }
184
185     ensureTrack()->scheduleLoad(url);
186 }
187
188 bool HTMLTrackElement::canLoadUrl(const KURL& url)
189 {
190     HTMLMediaElement* parent = mediaElement();
191     if (!parent)
192         return false;
193
194     // 4.8.10.12.3 Sourcing out-of-band text tracks
195
196     // 4. Download: If URL is not the empty string, perform a potentially CORS-enabled fetch of URL, with the
197     // mode being the state of the media element's crossorigin content attribute, the origin being the
198     // origin of the media element's Document, and the default origin behaviour set to fail.
199     if (url.isEmpty())
200         return false;
201
202     if (!document().contentSecurityPolicy()->allowMediaFromSource(url)) {
203         WTF_LOG(Media, "HTMLTrackElement::canLoadUrl(%s) -> rejected by Content Security Policy", urlForLoggingTrack(url).utf8().data());
204         return false;
205     }
206
207     return true;
208 }
209
210 void HTMLTrackElement::didCompleteLoad(LoadStatus status)
211 {
212     // 4.8.10.12.3 Sourcing out-of-band text tracks (continued)
213
214     // 4. Download: ...
215     // If the fetching algorithm fails for any reason (network error, the server returns an error
216     // code, a cross-origin check fails, etc), or if URL is the empty string or has the wrong origin
217     // as determined by the condition at the start of this step, or if the fetched resource is not in
218     // a supported format, then queue a task to first change the text track readiness state to failed
219     // to load and then fire a simple event named error at the track element; and then, once that task
220     // is queued, move on to the step below labeled monitoring.
221
222     if (status == Failure) {
223         setReadyState(HTMLTrackElement::TRACK_ERROR);
224         dispatchEvent(Event::create(EventTypeNames::error), IGNORE_EXCEPTION);
225         return;
226     }
227
228     // If the fetching algorithm does not fail, then the final task that is queued by the networking
229     // task source must run the following steps:
230     //     1. Change the text track readiness state to loaded.
231     setReadyState(HTMLTrackElement::LOADED);
232
233     //     2. If the file was successfully processed, fire a simple event named load at the
234     //        track element.
235     dispatchEvent(Event::create(EventTypeNames::load), IGNORE_EXCEPTION);
236 }
237
238 // NOTE: The values in the TextTrack::ReadinessState enum must stay in sync with those in HTMLTrackElement::ReadyState.
239 COMPILE_ASSERT(HTMLTrackElement::NONE == static_cast<HTMLTrackElement::ReadyState>(TextTrack::NotLoaded), TextTrackEnumNotLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumNONE);
240 COMPILE_ASSERT(HTMLTrackElement::LOADING == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loading), TextTrackEnumLoadingIsWrong_ShouldBe_HTMLTrackElementEnumLOADING);
241 COMPILE_ASSERT(HTMLTrackElement::LOADED == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loaded), TextTrackEnumLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumLOADED);
242 COMPILE_ASSERT(HTMLTrackElement::TRACK_ERROR == static_cast<HTMLTrackElement::ReadyState>(TextTrack::FailedToLoad), TextTrackEnumFailedToLoad_Is_Wrong_Should_Be_HTMLTrackElementEnumTRACK_ERROR);
243
244 void HTMLTrackElement::setReadyState(ReadyState state)
245 {
246     ensureTrack()->setReadinessState(static_cast<TextTrack::ReadinessState>(state));
247     if (HTMLMediaElement* parent = mediaElement())
248         return parent->textTrackReadyStateChanged(m_track.get());
249 }
250
251 HTMLTrackElement::ReadyState HTMLTrackElement::readyState()
252 {
253     return static_cast<ReadyState>(ensureTrack()->readinessState());
254 }
255
256 const AtomicString& HTMLTrackElement::mediaElementCrossOriginAttribute() const
257 {
258     if (HTMLMediaElement* parent = mediaElement())
259         return parent->fastGetAttribute(HTMLNames::crossoriginAttr);
260
261     return nullAtom;
262 }
263
264 HTMLMediaElement* HTMLTrackElement::mediaElement() const
265 {
266     Element* parent = parentElement();
267     if (isHTMLMediaElement(parent))
268         return toHTMLMediaElement(parent);
269     return 0;
270 }
271
272 void HTMLTrackElement::trace(Visitor* visitor)
273 {
274     visitor->trace(m_track);
275     HTMLElement::trace(visitor);
276 }
277
278 }