Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLTrackElement.cpp
blob2ffaece238460ab5f9e1694e13b9bacc1acf092b
1 /*
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
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.
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.
26 #include "config.h"
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"
37 namespace blink {
39 using namespace HTMLNames;
41 #if !LOG_DISABLED
42 static String urlForLoggingTrack(const KURL& url)
44 static const unsigned maximumURLLengthForLogging = 128;
46 if (url.string().length() < maximumURLLengthForLogging)
47 return url.string();
48 return url.string().substring(0, maximumURLLengthForLogging) + "...";
50 #endif
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()
63 #if !ENABLE(OILPAN)
64 if (m_track)
65 m_track->clearTrackElement();
66 #endif
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.
74 scheduleLoad();
76 HTMLElement::insertedInto(insertionPoint);
77 HTMLMediaElement* parent = mediaElement();
78 if (insertionPoint == parent)
79 parent->didAddTrackElement(this);
80 return InsertionDone;
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) {
93 if (!value.isEmpty())
94 scheduleLoad();
95 else if (m_track)
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()
125 if (!m_track) {
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())
149 return;
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())
153 return;
155 // 3. If the text track's track element does not have a media element as a parent, abort these steps.
156 if (!mediaElement())
157 return;
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
185 // fail.
186 if (!canLoadUrl(url)) {
187 didCompleteLoad(Failure);
188 return;
191 if (url == m_url) {
192 ASSERT(m_loader);
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.
197 break;
198 case TextTrackLoader::Finished:
199 didCompleteLoad(Success);
200 break;
201 case TextTrackLoader::Failed:
202 didCompleteLoad(Failure);
203 break;
204 default:
205 ASSERT_NOT_REACHED();
207 return;
210 m_url = url;
212 if (m_loader)
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();
223 if (!parent)
224 return false;
226 if (url.isEmpty())
227 return false;
229 if (!document().contentSecurityPolicy()->allowMediaFromSource(url)) {
230 WTF_LOG(Media, "HTMLTrackElement::canLoadUrl(%s) -> rejected by Content Security Policy", urlForLoggingTrack(url).utf8().data());
231 return false;
234 return true;
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));
258 return;
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);
271 ASSERT(m_track);
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);
282 ASSERT(m_track);
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);
320 return nullAtom;
323 HTMLMediaElement* HTMLTrackElement::mediaElement() const
325 Element* parent = parentElement();
326 if (isHTMLMediaElement(parent))
327 return toHTMLMediaElement(parent);
328 return nullptr;
331 DEFINE_TRACE(HTMLTrackElement)
333 visitor->trace(m_track);
334 visitor->trace(m_loader);
335 HTMLElement::trace(visitor);