2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "core/html/HTMLTrackElement.h"
29 #include "core/HTMLNames.h"
30 #include "core/dom/Document.h"
31 #include "core/events/Event.h"
32 #include "core/frame/csp/ContentSecurityPolicy.h"
33 #include "core/html/HTMLMediaElement.h"
34 #include "core/html/track/LoadableTextTrack.h"
35 #include "platform/Logging.h"
39 using namespace HTMLNames
;
42 static String
urlForLoggingTrack(const KURL
& url
)
44 static const unsigned maximumURLLengthForLogging
= 128;
46 if (url
.string().length() < maximumURLLengthForLogging
)
48 return url
.string().substring(0, maximumURLLengthForLogging
) + "...";
52 inline HTMLTrackElement::HTMLTrackElement(Document
& document
)
53 : HTMLElement(trackTag
, document
)
54 , m_loadTimer(this, &HTMLTrackElement::loadTimerFired
)
56 WTF_LOG(Media
, "HTMLTrackElement::HTMLTrackElement - %p", this);
59 DEFINE_NODE_FACTORY(HTMLTrackElement
)
61 HTMLTrackElement::~HTMLTrackElement()
65 m_track
->clearTrackElement();
69 Node::InsertionNotificationRequest
HTMLTrackElement::insertedInto(ContainerNode
* insertionPoint
)
71 WTF_LOG(Media
, "HTMLTrackElement::insertedInto");
73 // Since we've moved to a new parent, we may now be able to load.
76 HTMLElement::insertedInto(insertionPoint
);
77 HTMLMediaElement
* parent
= mediaElement();
78 if (insertionPoint
== parent
)
79 parent
->didAddTrackElement(this);
83 void HTMLTrackElement::removedFrom(ContainerNode
* insertionPoint
)
85 if (!parentNode() && isHTMLMediaElement(*insertionPoint
))
86 toHTMLMediaElement(insertionPoint
)->didRemoveTrackElement(this);
87 HTMLElement::removedFrom(insertionPoint
);
90 void HTMLTrackElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
92 if (name
== srcAttr
) {
96 m_track
->removeAllCues();
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
);
110 HTMLElement::parseAttribute(name
, value
);
113 const AtomicString
& HTMLTrackElement::kind()
115 return track()->kind();
118 void HTMLTrackElement::setKind(const AtomicString
& kind
)
120 setAttribute(kindAttr
, kind
);
123 LoadableTextTrack
* HTMLTrackElement::ensureTrack()
126 // kind, label and language are updated by parseAttribute
127 m_track
= LoadableTextTrack::create(this);
129 return m_track
.get();
132 TextTrack
* HTMLTrackElement::track()
134 return ensureTrack();
137 bool HTMLTrackElement::isURLAttribute(const Attribute
& attribute
) const
139 return attribute
.name() == srcAttr
|| HTMLElement::isURLAttribute(attribute
);
142 void HTMLTrackElement::scheduleLoad()
144 WTF_LOG(Media
, "HTMLTrackElement::scheduleLoad");
146 // 1. If another occurrence of this algorithm is already running for this text track and its track element,
147 // abort these steps, letting that other algorithm take care of this element.
148 if (m_loadTimer
.isActive())
151 // 2. If the text track's text track mode is not set to one of hidden or showing, abort these steps.
152 if (ensureTrack()->mode() != TextTrack::hiddenKeyword() && ensureTrack()->mode() != TextTrack::showingKeyword())
155 // 3. If the text track's track element does not have a media element as a parent, abort these steps.
159 // 4. Run the remainder of these steps in parallel, allowing whatever caused these steps to run to continue.
160 m_loadTimer
.startOneShot(0, FROM_HERE
);
162 // 5. Top: Await a stable state. The synchronous section consists of the following steps. (The steps in the
163 // synchronous section are marked with [X])
164 // FIXME: We use a timer to approximate a "stable state" - i.e. this is not 100% per spec.
167 void HTMLTrackElement::loadTimerFired(Timer
<HTMLTrackElement
>*)
169 WTF_LOG(Media
, "HTMLTrackElement::loadTimerFired");
171 // 6. [X] Set the text track readiness state to loading.
172 setReadyState(LOADING
);
174 // 7. [X] Let URL be the track URL of the track element.
175 KURL url
= getNonEmptyURLAttribute(srcAttr
);
177 // 8. [X] If the track element's parent is a media element then let CORS mode be the state of the parent media
178 // element's crossorigin content attribute. Otherwise, let CORS mode be No CORS.
179 const AtomicString
& corsMode
= mediaElementCrossOriginAttribute();
181 // 9. End the synchronous section, continuing the remaining steps in parallel.
183 // 10. If URL is not the empty string, perform a potentially CORS-enabled fetch of URL, with the mode being CORS
184 // mode, the origin being the origin of the track element's node document, and the default origin behaviour set to
186 if (!canLoadUrl(url
)) {
187 didCompleteLoad(Failure
);
193 switch (m_loader
->loadState()) {
194 case TextTrackLoader::Idle
:
195 case TextTrackLoader::Loading
:
196 // If loading of the resource from this URL is in progress, return early.
198 case TextTrackLoader::Finished
:
199 didCompleteLoad(Success
);
201 case TextTrackLoader::Failed
:
202 didCompleteLoad(Failure
);
205 ASSERT_NOT_REACHED();
213 m_loader
->cancelLoad();
215 m_loader
= TextTrackLoader::create(*this, document());
216 if (!m_loader
->load(m_url
, corsMode
))
217 didCompleteLoad(Failure
);
220 bool HTMLTrackElement::canLoadUrl(const KURL
& url
)
222 HTMLMediaElement
* parent
= mediaElement();
229 if (!document().contentSecurityPolicy()->allowMediaFromSource(url
)) {
230 WTF_LOG(Media
, "HTMLTrackElement::canLoadUrl(%s) -> rejected by Content Security Policy", urlForLoggingTrack(url
).utf8().data());
237 void HTMLTrackElement::didCompleteLoad(LoadStatus status
)
239 // 10. ... (continued)
241 // If the fetching algorithm fails for any reason (network error, the server returns an error code, a cross-origin
242 // check fails, etc), or if URL is the empty string, then queue a task to first change the text track readiness
243 // state to failed to load and then fire a simple event named error at the track element. This task must use the DOM
244 // manipulation task source.
246 // (Note: We don't "queue a task" here because this method will only be called from a timer - m_loadTimer or
247 // TextTrackLoader::m_cueLoadTimer - which should be a reasonable, and hopefully non-observable, approximation of
248 // the spec text. I.e we could consider this to be run from the "networking task source".)
250 // If the fetching algorithm does not fail, but the type of the resource is not a supported text track format, or
251 // the file was not successfully processed (e.g. the format in question is an XML format and the file contained a
252 // well-formedness error that the XML specification requires be detected and reported to the application), then the
253 // task that is queued by the networking task source in which the aforementioned problem is found must change the
254 // text track readiness state to failed to load and fire a simple event named error at the track element.
255 if (status
== Failure
) {
256 setReadyState(TRACK_ERROR
);
257 dispatchEvent(Event::create(EventTypeNames::error
));
261 // If the fetching algorithm does not fail, and the file was successfully processed, then the final task that is
262 // queued by the networking task source, after it has finished parsing the data, must change the text track
263 // readiness state to loaded, and fire a simple event named load at the track element.
264 setReadyState(LOADED
);
265 dispatchEvent(Event::create(EventTypeNames::load
));
268 void HTMLTrackElement::newCuesAvailable(TextTrackLoader
* loader
)
270 ASSERT_UNUSED(loader
, m_loader
== loader
);
273 HeapVector
<Member
<TextTrackCue
>> newCues
;
274 m_loader
->getNewCues(newCues
);
276 m_track
->addListOfCues(newCues
);
279 void HTMLTrackElement::newRegionsAvailable(TextTrackLoader
* loader
)
281 ASSERT_UNUSED(loader
, m_loader
== loader
);
284 HeapVector
<Member
<VTTRegion
>> newRegions
;
285 m_loader
->getNewRegions(newRegions
);
287 m_track
->addRegions(newRegions
);
290 void HTMLTrackElement::cueLoadingCompleted(TextTrackLoader
* loader
, bool loadingFailed
)
292 ASSERT_UNUSED(loader
, m_loader
== loader
);
294 didCompleteLoad(loadingFailed
? Failure
: Success
);
297 // NOTE: The values in the TextTrack::ReadinessState enum must stay in sync with those in HTMLTrackElement::ReadyState.
298 static_assert(HTMLTrackElement::NONE
== static_cast<HTMLTrackElement::ReadyState
>(TextTrack::NotLoaded
), "HTMLTrackElement::NONE should be in sync with TextTrack::NotLoaded");
299 static_assert(HTMLTrackElement::LOADING
== static_cast<HTMLTrackElement::ReadyState
>(TextTrack::Loading
), "HTMLTrackElement::LOADING should be in sync with TextTrack::Loading");
300 static_assert(HTMLTrackElement::LOADED
== static_cast<HTMLTrackElement::ReadyState
>(TextTrack::Loaded
), "HTMLTrackElement::LOADED should be in sync with TextTrack::Loaded");
301 static_assert(HTMLTrackElement::TRACK_ERROR
== static_cast<HTMLTrackElement::ReadyState
>(TextTrack::FailedToLoad
), "HTMLTrackElement::TRACK_ERROR should be in sync with TextTrack::FailedToLoad");
303 void HTMLTrackElement::setReadyState(ReadyState state
)
305 ensureTrack()->setReadinessState(static_cast<TextTrack::ReadinessState
>(state
));
306 if (HTMLMediaElement
* parent
= mediaElement())
307 return parent
->textTrackReadyStateChanged(m_track
.get());
310 HTMLTrackElement::ReadyState
HTMLTrackElement::readyState()
312 return static_cast<ReadyState
>(ensureTrack()->readinessState());
315 const AtomicString
& HTMLTrackElement::mediaElementCrossOriginAttribute() const
317 if (HTMLMediaElement
* parent
= mediaElement())
318 return parent
->fastGetAttribute(HTMLNames::crossoriginAttr
);
323 HTMLMediaElement
* HTMLTrackElement::mediaElement() const
325 Element
* parent
= parentElement();
326 if (isHTMLMediaElement(parent
))
327 return toHTMLMediaElement(parent
);
331 DEFINE_TRACE(HTMLTrackElement
)
333 visitor
->trace(m_track
);
334 visitor
->trace(m_loader
);
335 HTMLElement::trace(visitor
);