Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(377)

Side by Side Diff: sky/engine/core/html/HTMLMediaElement.cpp

Issue 689373003: Remove most of the media stack. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/engine/core/html/HTMLMediaElement.h ('k') | sky/engine/core/html/HTMLMediaElement.idl » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple 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/HTMLMediaElement.h"
28
29 #include "bindings/core/v8/ExceptionMessages.h"
30 #include "bindings/core/v8/ExceptionState.h"
31 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
32 #include "bindings/core/v8/ScriptController.h"
33 #include "core/HTMLNames.h"
34 #include "core/css/MediaList.h"
35 #include "core/dom/Attribute.h"
36 #include "core/dom/ElementTraversal.h"
37 #include "core/dom/ExceptionCode.h"
38 #include "core/events/Event.h"
39 #include "core/frame/LocalFrame.h"
40 #include "core/frame/Settings.h"
41 #include "core/frame/UseCounter.h"
42 #include "core/html/HTMLMediaSource.h"
43 #include "core/html/HTMLSourceElement.h"
44 #include "core/html/MediaError.h"
45 #include "core/html/MediaFragmentURIParser.h"
46 #include "core/html/TimeRanges.h"
47 #include "core/rendering/RenderView.h"
48 #include "core/rendering/compositing/RenderLayerCompositor.h"
49 #include "platform/ContentType.h"
50 #include "platform/Language.h"
51 #include "platform/Logging.h"
52 #include "platform/MIMETypeFromURL.h"
53 #include "platform/MIMETypeRegistry.h"
54 #include "platform/NotImplemented.h"
55 #include "platform/RuntimeEnabledFeatures.h"
56 #include "platform/UserGestureIndicator.h"
57 #include "platform/graphics/GraphicsLayer.h"
58 #include "public/platform/Platform.h"
59 #include "wtf/CurrentTime.h"
60 #include "wtf/MathExtras.h"
61 #include "wtf/NonCopyingSort.h"
62 #include "wtf/Uint8Array.h"
63 #include "wtf/text/CString.h"
64 #include <limits>
65
66 using blink::WebMediaPlayer;
67 using blink::WebMimeRegistry;
68 using blink::WebMediaPlayerClient;
69
70 namespace blink {
71
72 #if !LOG_DISABLED
73 static String urlForLoggingMedia(const KURL& url)
74 {
75 static const unsigned maximumURLLengthForLogging = 128;
76
77 if (url.string().length() < maximumURLLengthForLogging)
78 return url.string();
79 return url.string().substring(0, maximumURLLengthForLogging) + "...";
80 }
81
82 static const char* boolString(bool val)
83 {
84 return val ? "true" : "false";
85 }
86 #endif
87
88 #ifndef LOG_MEDIA_EVENTS
89 // Default to not logging events because so many are generated they can overwhel m the rest of
90 // the logging.
91 #define LOG_MEDIA_EVENTS 0
92 #endif
93
94 #ifndef LOG_CACHED_TIME_WARNINGS
95 // Default to not logging warnings about excessive drift in the cached media tim e because it adds a
96 // fair amount of overhead and logging.
97 #define LOG_CACHED_TIME_WARNINGS 0
98 #endif
99
100 typedef HashSet<RawPtr<HTMLMediaElement> > WeakMediaElementSet;
101 typedef HashMap<RawPtr<Document>, WeakMediaElementSet> DocumentElementSetMap;
102 static DocumentElementSetMap& documentToElementSetMap()
103 {
104 DEFINE_STATIC_LOCAL(OwnPtr<DocumentElementSetMap>, map, (adoptPtr(new Docume ntElementSetMap())));
105 return *map;
106 }
107
108 static void addElementToDocumentMap(HTMLMediaElement* element, Document* documen t)
109 {
110 DocumentElementSetMap& map = documentToElementSetMap();
111 WeakMediaElementSet set = map.take(document);
112 set.add(element);
113 map.add(document, set);
114 }
115
116 static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* do cument)
117 {
118 DocumentElementSetMap& map = documentToElementSetMap();
119 WeakMediaElementSet set = map.take(document);
120 set.remove(element);
121 if (!set.isEmpty())
122 map.add(document, set);
123 }
124
125 static bool canLoadURL(const KURL& url, const ContentType& contentType, const St ring& keySystem)
126 {
127 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
128
129 String contentMIMEType = contentType.type().lower();
130 String contentTypeCodecs = contentType.parameter(codecs);
131
132 // If the MIME type is missing or is not meaningful, try to figure it out fr om the URL.
133 if (contentMIMEType.isEmpty() || contentMIMEType == "application/octet-strea m" || contentMIMEType == "text/plain") {
134 if (url.protocolIsData())
135 contentMIMEType = mimeTypeFromDataURL(url.string());
136 }
137
138 // If no MIME type is specified, always attempt to load.
139 if (contentMIMEType.isEmpty())
140 return true;
141
142 // 4.8.10.3 MIME types - In the absence of a specification to the contrary, the MIME type "application/octet-stream"
143 // when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows
144 // it cannot render.
145 if (contentMIMEType != "application/octet-stream" || contentTypeCodecs.isEmp ty()) {
146 WebMimeRegistry::SupportsType supported = blink::Platform::current()->mi meRegistry()->supportsMediaMIMEType(contentMIMEType, contentTypeCodecs, keySyste m.lower());
147 return supported > WebMimeRegistry::IsNotSupported;
148 }
149
150 return false;
151 }
152
153 WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType& contentType, const String& keySystem)
154 {
155 DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
156
157 if (!RuntimeEnabledFeatures::mediaEnabled())
158 return WebMimeRegistry::IsNotSupported;
159
160 String type = contentType.type().lower();
161 // The codecs string is not lower-cased because MP4 values are case sensitiv e
162 // per http://tools.ietf.org/html/rfc4281#page-7.
163 String typeCodecs = contentType.parameter(codecs);
164 String system = keySystem.lower();
165
166 if (type.isEmpty())
167 return WebMimeRegistry::IsNotSupported;
168
169 // 4.8.10.3 MIME types - The canPlayType(type) method must return the empty string if type is a type that the
170 // user agent knows it cannot render or is the type "application/octet-strea m"
171 if (type == "application/octet-stream")
172 return WebMimeRegistry::IsNotSupported;
173
174 return blink::Platform::current()->mimeRegistry()->supportsMediaMIMEType(typ e, typeCodecs, system);
175 }
176
177 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum ent)
178 : HTMLElement(tagName, document)
179 , ActiveDOMObject(&document)
180 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
181 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
182 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFire d)
183 , m_playedTimeRanges()
184 , m_asyncEventQueue(GenericEventQueue::create(this))
185 , m_playbackRate(1.0f)
186 , m_defaultPlaybackRate(1.0f)
187 , m_networkState(NETWORK_EMPTY)
188 , m_readyState(HAVE_NOTHING)
189 , m_readyStateMaximum(HAVE_NOTHING)
190 , m_volume(1.0f)
191 , m_lastSeekTime(0)
192 , m_previousProgressTime(std::numeric_limits<double>::max())
193 , m_duration(std::numeric_limits<double>::quiet_NaN())
194 , m_lastTimeUpdateEventWallTime(0)
195 , m_lastTimeUpdateEventMovieTime(std::numeric_limits<double>::max())
196 , m_loadState(WaitingForSource)
197 , m_deferredLoadState(NotDeferred)
198 , m_deferredLoadTimer(this, &HTMLMediaElement::deferredLoadTimerFired)
199 , m_webLayer(0)
200 , m_preload(MediaPlayer::Auto)
201 , m_displayMode(Unknown)
202 , m_cachedTime(MediaPlayer::invalidTime())
203 , m_fragmentStartTime(MediaPlayer::invalidTime())
204 , m_fragmentEndTime(MediaPlayer::invalidTime())
205 , m_pendingActionFlags(0)
206 , m_userGestureRequiredForPlay(false)
207 , m_playing(false)
208 , m_shouldDelayLoadEvent(false)
209 , m_haveFiredLoadedData(false)
210 , m_active(true)
211 , m_autoplaying(true)
212 , m_muted(false)
213 , m_paused(true)
214 , m_seeking(false)
215 , m_sentStalledEvent(false)
216 , m_sentEndEvent(false)
217 , m_pausedInternal(false)
218 , m_closedCaptionsVisible(false)
219 , m_completelyLoaded(false)
220 , m_havePreparedToPlay(false)
221 , m_processingPreferenceChange(false)
222 #if ENABLE(OILPAN)
223 , m_isFinalizing(false)
224 , m_closeMediaSourceWhenFinalizing(false)
225 #endif
226 {
227 ASSERT(RuntimeEnabledFeatures::mediaEnabled());
228
229 WTF_LOG(Media, "HTMLMediaElement::HTMLMediaElement");
230 ScriptWrappable::init(this);
231
232 if (document.settings() && document.settings()->mediaPlaybackRequiresUserGes ture())
233 m_userGestureRequiredForPlay = true;
234
235 setHasCustomStyleCallbacks();
236 addElementToDocumentMap(this, &document);
237 }
238
239 HTMLMediaElement::~HTMLMediaElement()
240 {
241 WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
242
243 #if ENABLE(OILPAN)
244 // If the HTMLMediaElement dies with the document we are not
245 // allowed to touch the document to adjust delay load event counts
246 // because the document could have been already
247 // destructed. However, if the HTMLMediaElement dies with the
248 // document there is no need to change the delayed load counts
249 // because no load event will fire anyway. If the document is
250 // still alive we do have to decrement the load delay counts. We
251 // determine if the document is alive via the ActiveDOMObject
252 // which is a context lifecycle observer. If the Document has been
253 // destructed ActiveDOMObject::executionContext() returns 0.
254 if (ActiveDOMObject::executionContext())
255 setShouldDelayLoadEvent(false);
256 #else
257 // HTMLMediaElement and m_asyncEventQueue always become unreachable
258 // together. So HTMLMediaElemenet and m_asyncEventQueue are destructed in
259 // the same GC. We don't need to close it explicitly in Oilpan.
260 m_asyncEventQueue->close();
261
262 setShouldDelayLoadEvent(false);
263 #endif
264
265 #if ENABLE(OILPAN)
266 if (m_closeMediaSourceWhenFinalizing)
267 closeMediaSource();
268 #else
269 closeMediaSource();
270
271 removeElementFromDocumentMap(this, &document());
272 #endif
273
274 // Destroying the player may cause a resource load to be canceled,
275 // which could result in userCancelledLoad() being called back.
276 // Setting m_completelyLoaded ensures that such a call will not cause
277 // us to dispatch an abort event, which would result in a crash.
278 // See http://crbug.com/233654 for more details.
279 m_completelyLoaded = true;
280
281 // With Oilpan load events on the Document are always delayed during
282 // sweeping so we don't need to explicitly increment and decrement
283 // load event delay counts.
284 #if !ENABLE(OILPAN)
285 // Destroying the player may cause a resource load to be canceled,
286 // which could result in Document::dispatchWindowLoadEvent() being
287 // called via ResourceFetch::didLoadResource() then
288 // FrameLoader::loadDone(). To prevent load event dispatching during
289 // object destruction, we use Document::incrementLoadEventDelayCount().
290 // See http://crbug.com/275223 for more details.
291 document().incrementLoadEventDelayCount();
292 #endif
293
294 #if ENABLE(OILPAN)
295 // Oilpan: the player must be released, but the player object
296 // cannot safely access this player client any longer as parts of
297 // it may have been finalized already (like the media element's
298 // supplementable table.) Handled for now by entering an
299 // is-finalizing state, which is explicitly checked for if the
300 // player tries to access the media element during shutdown.
301 //
302 // FIXME: Oilpan: move the media player to the heap instead and
303 // avoid having to finalize it from here; this whole #if block
304 // could then be removed (along with the state bit it depends on.)
305 // crbug.com/378229
306 m_isFinalizing = true;
307 #endif
308
309 clearMediaPlayerAndAudioSourceProviderClientWithoutLocking();
310
311 #if !ENABLE(OILPAN)
312 document().decrementLoadEventDelayCount();
313 #endif
314 }
315
316 #if ENABLE(OILPAN)
317 void HTMLMediaElement::setCloseMediaSourceWhenFinalizing()
318 {
319 ASSERT(!m_closeMediaSourceWhenFinalizing);
320 m_closeMediaSourceWhenFinalizing = true;
321 }
322 #endif
323
324 void HTMLMediaElement::didMoveToNewDocument(Document& oldDocument)
325 {
326 WTF_LOG(Media, "HTMLMediaElement::didMoveToNewDocument");
327
328 if (m_shouldDelayLoadEvent) {
329 document().incrementLoadEventDelayCount();
330 // Note: Keeping the load event delay count increment on oldDocument tha t was added
331 // when m_shouldDelayLoadEvent was set so that destruction of m_player c an not
332 // cause load event dispatching in oldDocument.
333 } else {
334 // Incrementing the load event delay count so that destruction of m_play er can not
335 // cause load event dispatching in oldDocument.
336 oldDocument.incrementLoadEventDelayCount();
337 }
338
339 removeElementFromDocumentMap(this, &oldDocument);
340 addElementToDocumentMap(this, &document());
341
342 // FIXME: This is a temporary fix to prevent this object from causing the
343 // MediaPlayer to dereference LocalFrame and FrameLoader pointers from the
344 // previous document. A proper fix would provide a mechanism to allow this
345 // object to refresh the MediaPlayer's LocalFrame and FrameLoader references on
346 // document changes so that playback can be resumed properly.
347 userCancelledLoad();
348
349 // Decrement the load event delay count on oldDocument now that m_player has been destroyed
350 // and there is no risk of dispatching a load event from within the destruct or.
351 oldDocument.decrementLoadEventDelayCount();
352
353 ActiveDOMObject::didMoveToNewExecutionContext(&document());
354 HTMLElement::didMoveToNewDocument(oldDocument);
355 }
356
357 bool HTMLMediaElement::isMouseFocusable() const
358 {
359 return false;
360 }
361
362 void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicStr ing& value)
363 {
364 if (name == HTMLNames::srcAttr) {
365 // Trigger a reload, as long as the 'src' attribute is present.
366 if (!value.isNull()) {
367 clearMediaPlayer(LoadMediaResource);
368 scheduleDelayedAction(LoadMediaResource);
369 }
370 } else if (name == HTMLNames::preloadAttr) {
371 if (equalIgnoringCase(value, "none"))
372 m_preload = MediaPlayer::None;
373 else if (equalIgnoringCase(value, "metadata"))
374 m_preload = MediaPlayer::MetaData;
375 else {
376 // The spec does not define an "invalid value default" but "auto" is suggested as the
377 // "missing value default", so use it for everything except "none" a nd "metadata"
378 m_preload = MediaPlayer::Auto;
379 }
380
381 // The attribute must be ignored if the autoplay attribute is present
382 if (!autoplay() && m_player)
383 setPlayerPreload();
384 }
385
386 HTMLElement::parseAttribute(name, value);
387 }
388
389 void HTMLMediaElement::finishParsingChildren()
390 {
391 HTMLElement::finishParsingChildren();
392 }
393
394 bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
395 {
396 // FIXME(sky): Can we delete this method?
397 return false;
398 }
399
400 RenderObject* HTMLMediaElement::createRenderer(RenderStyle*)
401 {
402 return 0;
403 }
404
405 Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode* insertionPoint)
406 {
407 WTF_LOG(Media, "HTMLMediaElement::insertedInto");
408
409 HTMLElement::insertedInto(insertionPoint);
410 if (insertionPoint->inDocument()) {
411 m_active = true;
412
413 if (!getAttribute(HTMLNames::srcAttr).isEmpty() && m_networkState == NET WORK_EMPTY)
414 scheduleDelayedAction(LoadMediaResource);
415 }
416
417 return InsertionShouldCallDidNotifySubtreeInsertions;
418 }
419
420 void HTMLMediaElement::didNotifySubtreeInsertionsToDocument()
421 {
422 }
423
424 void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint)
425 {
426 WTF_LOG(Media, "HTMLMediaElement::removedFrom");
427
428 m_active = false;
429 if (insertionPoint->inDocument() && insertionPoint->document().isActive()) {
430 if (m_networkState > NETWORK_EMPTY)
431 pause();
432 }
433
434 HTMLElement::removedFrom(insertionPoint);
435 }
436
437 void HTMLMediaElement::attach(const AttachContext& context)
438 {
439 HTMLElement::attach(context);
440
441 if (renderer())
442 renderer()->updateFromElement();
443 }
444
445 void HTMLMediaElement::didRecalcStyle(StyleRecalcChange)
446 {
447 if (renderer())
448 renderer()->updateFromElement();
449 }
450
451 void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
452 {
453 WTF_LOG(Media, "HTMLMediaElement::scheduleDelayedAction");
454
455 if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaRe source)) {
456 prepareForLoad();
457 m_pendingActionFlags |= LoadMediaResource;
458 }
459
460 if (!m_loadTimer.isActive())
461 m_loadTimer.startOneShot(0, FROM_HERE);
462 }
463
464 void HTMLMediaElement::scheduleNextSourceChild()
465 {
466 // Schedule the timer to try the next <source> element WITHOUT resetting sta te ala prepareForLoad.
467 m_pendingActionFlags |= LoadMediaResource;
468 m_loadTimer.startOneShot(0, FROM_HERE);
469 }
470
471 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
472 {
473 scheduleEvent(Event::createCancelable(eventName));
474 }
475
476 void HTMLMediaElement::scheduleEvent(PassRefPtr<Event> event)
477 {
478 #if LOG_MEDIA_EVENTS
479 WTF_LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", event->t ype().ascii().data());
480 #endif
481 m_asyncEventQueue->enqueueEvent(event);
482 }
483
484 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
485 {
486 if (m_pendingActionFlags & LoadMediaResource) {
487 if (m_loadState == LoadingFromSourceElement)
488 loadNextSourceChild();
489 else
490 loadInternal();
491 }
492
493 m_pendingActionFlags = 0;
494 }
495
496 PassRefPtr<MediaError> HTMLMediaElement::error() const
497 {
498 return m_error;
499 }
500
501 void HTMLMediaElement::setSrc(const AtomicString& url)
502 {
503 setAttribute(HTMLNames::srcAttr, url);
504 }
505
506 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
507 {
508 return m_networkState;
509 }
510
511 String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySy stem) const
512 {
513 if (!keySystem.isNull())
514 UseCounter::count(document(), UseCounter::CanPlayTypeKeySystem);
515
516 WebMimeRegistry::SupportsType support = supportsType(ContentType(mimeType), keySystem);
517 String canPlay;
518
519 // 4.8.10.3
520 switch (support)
521 {
522 case WebMimeRegistry::IsNotSupported:
523 canPlay = emptyString();
524 break;
525 case WebMimeRegistry::MayBeSupported:
526 canPlay = "maybe";
527 break;
528 case WebMimeRegistry::IsSupported:
529 canPlay = "probably";
530 break;
531 }
532
533 WTF_LOG(Media, "HTMLMediaElement::canPlayType(%s, %s) -> %s", mimeType.utf8( ).data(), keySystem.utf8().data(), canPlay.utf8().data());
534
535 return canPlay;
536 }
537
538 void HTMLMediaElement::load()
539 {
540 WTF_LOG(Media, "HTMLMediaElement::load()");
541
542 if (UserGestureIndicator::processingUserGesture())
543 m_userGestureRequiredForPlay = false;
544
545 prepareForLoad();
546 loadInternal();
547 prepareToPlay();
548 }
549
550 void HTMLMediaElement::prepareForLoad()
551 {
552 WTF_LOG(Media, "HTMLMediaElement::prepareForLoad");
553
554 // Perform the cleanup required for the resource load algorithm to run.
555 stopPeriodicTimers();
556 m_loadTimer.stop();
557 cancelDeferredLoad();
558 // FIXME: Figure out appropriate place to reset LoadTextTrackResource if nec essary and set m_pendingActionFlags to 0 here.
559 m_pendingActionFlags &= ~LoadMediaResource;
560 m_sentEndEvent = false;
561 m_sentStalledEvent = false;
562 m_haveFiredLoadedData = false;
563 m_completelyLoaded = false;
564 m_havePreparedToPlay = false;
565 m_displayMode = Unknown;
566
567 // 1 - Abort any already-running instance of the resource selection algorith m for this element.
568 m_loadState = WaitingForSource;
569 m_currentSourceNode = nullptr;
570
571 // 2 - If there are any tasks from the media element's media element event t ask source in
572 // one of the task queues, then remove those tasks.
573 cancelPendingEventsAndCallbacks();
574
575 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETW ORK_IDLE, queue
576 // a task to fire a simple event named abort at the media element.
577 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
578 scheduleEvent(EventTypeNames::abort);
579
580 createMediaPlayer();
581
582 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
583 if (m_networkState != NETWORK_EMPTY) {
584 // 4.1 - Queue a task to fire a simple event named emptied at the media element.
585 scheduleEvent(EventTypeNames::emptied);
586
587 // 4.2 - If a fetching process is in progress for the media element, the user agent should stop it.
588 m_networkState = NETWORK_EMPTY;
589
590 // 4.3 - Forget the media element's media-resource-specific tracks.
591 // FIXME(sky): We have no tracks.
592
593 // 4.4 - If readyState is not set to HAVE_NOTHING, then set it to that s tate.
594 m_readyState = HAVE_NOTHING;
595 m_readyStateMaximum = HAVE_NOTHING;
596
597 // 4.5 - If the paused attribute is false, then set it to true.
598 m_paused = true;
599
600 // 4.6 - If seeking is true, set it to false.
601 m_seeking = false;
602
603 // 4.7 - Set the current playback position to 0.
604 // Set the official playback position to 0.
605 // If this changed the official playback position, then queue a ta sk to fire a simple event named timeupdate at the media element.
606 // FIXME: Add support for firing this event.
607
608 // 4.8 - Set the initial playback position to 0.
609 // FIXME: Make this less subtle. The position only becomes 0 because the ready state is HAVE_NOTHING.
610 invalidateCachedTime();
611
612 // 4.9 - Set the timeline offset to Not-a-Number (NaN).
613 // 4.10 - Update the duration attribute to Not-a-Number (NaN).
614 }
615
616 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRat e attribute.
617 setPlaybackRate(defaultPlaybackRate());
618
619 // 6 - Set the error attribute to null and the autoplaying flag to true.
620 m_error = nullptr;
621 m_autoplaying = true;
622
623 // 7 - Invoke the media element's resource selection algorithm.
624
625 // 8 - Note: Playback of any previously playing media resource for this elem ent stops.
626
627 // The resource selection algorithm
628 // 1 - Set the networkState to NETWORK_NO_SOURCE
629 m_networkState = NETWORK_NO_SOURCE;
630
631 // 2 - Asynchronously await a stable state.
632
633 m_playedTimeRanges = TimeRanges::create();
634
635 // FIXME: Investigate whether these can be moved into m_networkState != NETW ORK_EMPTY block above
636 // so they are closer to the relevant spec steps.
637 m_lastSeekTime = 0;
638 m_duration = std::numeric_limits<double>::quiet_NaN();
639
640 // The spec doesn't say to block the load event until we actually run the as ynchronous section
641 // algorithm, but do it now because we won't start that until after the time r fires and the
642 // event may have already fired by then.
643 setShouldDelayLoadEvent(true);
644 }
645
646 void HTMLMediaElement::loadInternal()
647 {
648 selectMediaResource();
649 }
650
651 void HTMLMediaElement::selectMediaResource()
652 {
653 WTF_LOG(Media, "HTMLMediaElement::selectMediaResource");
654
655 enum Mode { attribute, children };
656
657 // 3 - If the media element has a src attribute, then let mode be attribute.
658 Mode mode = attribute;
659 if (!hasAttribute(HTMLNames::srcAttr)) {
660 // Otherwise, if the media element does not have a src attribute but has a source
661 // element child, then let mode be children and let candidate be the fir st such
662 // source element child in tree order.
663 if (HTMLSourceElement* element = Traversal<HTMLSourceElement>::firstChil d(*this)) {
664 mode = children;
665 m_nextChildNodeToConsider = element;
666 m_currentSourceNode = nullptr;
667 } else {
668 // Otherwise the media element has neither a src attribute nor a sou rce element
669 // child: set the networkState to NETWORK_EMPTY, and abort these ste ps; the
670 // synchronous section ends.
671 m_loadState = WaitingForSource;
672 setShouldDelayLoadEvent(false);
673 m_networkState = NETWORK_EMPTY;
674
675 WTF_LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to lo ad");
676 return;
677 }
678 }
679
680 // 4 - Set the media element's delaying-the-load-event flag to true (this de lays the load event),
681 // and set its networkState to NETWORK_LOADING.
682 setShouldDelayLoadEvent(true);
683 m_networkState = NETWORK_LOADING;
684
685 // 5 - Queue a task to fire a simple event named loadstart at the media elem ent.
686 scheduleEvent(EventTypeNames::loadstart);
687
688 // 6 - If mode is attribute, then run these substeps
689 if (mode == attribute) {
690 m_loadState = LoadingFromSrcAttr;
691
692 // If the src attribute's value is the empty string ... jump down to the failed step below
693 KURL mediaURL = getNonEmptyURLAttribute(HTMLNames::srcAttr);
694 if (mediaURL.isEmpty()) {
695 mediaLoadingFailed(WebMediaPlayer::NetworkStateFormatError);
696 WTF_LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'") ;
697 return;
698 }
699
700 if (!isSafeToLoadURL(mediaURL, Complain)) {
701 mediaLoadingFailed(WebMediaPlayer::NetworkStateFormatError);
702 return;
703 }
704
705 // No type or key system information is available when the url comes
706 // from the 'src' attribute so MediaPlayer
707 // will have to pick a media engine based on the file extension.
708 ContentType contentType((String()));
709 loadResource(mediaURL, contentType, String());
710 WTF_LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attri bute url");
711 return;
712 }
713
714 // Otherwise, the source elements will be used
715 loadNextSourceChild();
716 }
717
718 void HTMLMediaElement::loadNextSourceChild()
719 {
720 ContentType contentType((String()));
721 String keySystem;
722 KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
723 if (!mediaURL.isValid()) {
724 waitForSourceChange();
725 return;
726 }
727
728 // Recreate the media player for the new url
729 createMediaPlayer();
730
731 m_loadState = LoadingFromSourceElement;
732 loadResource(mediaURL, contentType, keySystem);
733 }
734
735 void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType, c onst String& keySystem)
736 {
737 ASSERT(isSafeToLoadURL(url, Complain));
738
739 WTF_LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLoggingMe dia(url).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data() );
740
741 LocalFrame* frame = document().frame();
742 if (!frame) {
743 mediaLoadingFailed(WebMediaPlayer::NetworkStateFormatError);
744 return;
745 }
746
747 // The resource fetch algorithm
748 m_networkState = NETWORK_LOADING;
749
750 // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
751 // cache is an internal detail not exposed through the media element API.
752 m_currentSrc = url;
753
754 WTF_LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlFor LoggingMedia(m_currentSrc).utf8().data());
755
756 startProgressEventTimer();
757
758 // Reset display mode to force a recalculation of what to show because we ar e resetting the player.
759 setDisplayMode(Unknown);
760
761 if (!autoplay())
762 setPlayerPreload();
763
764 if (hasAttribute(HTMLNames::mutedAttr))
765 m_muted = true;
766 updateVolume();
767
768 ASSERT(!m_mediaSource);
769
770 bool attemptLoad = true;
771
772 if (attemptLoad && canLoadURL(url, contentType, keySystem)) {
773 ASSERT(!webMediaPlayer());
774
775 if (!m_havePreparedToPlay && !autoplay() && m_preload == MediaPlayer::No ne) {
776 WTF_LOG(Media, "HTMLMediaElement::loadResource : Delaying load becau se preload == 'none'");
777 deferLoad();
778 } else {
779 startPlayerLoad();
780 }
781 } else {
782 mediaLoadingFailed(WebMediaPlayer::NetworkStateFormatError);
783 }
784
785 // If there is no poster to display, allow the media engine to render video frames as soon as
786 // they are available.
787 updateDisplayState();
788
789 if (renderer())
790 renderer()->updateFromElement();
791 }
792
793 void HTMLMediaElement::startPlayerLoad()
794 {
795 // Filter out user:pass as those two URL components aren't
796 // considered for media resource fetches (including for the CORS
797 // use-credentials mode.) That behavior aligns with Gecko, with IE
798 // being more restrictive and not allowing fetches to such URLs.
799 //
800 // Spec reference: http://whatwg.org/c/#concept-media-load-resource
801 //
802 // FIXME: when the HTML spec switches to specifying resource
803 // fetches in terms of Fetch (http://fetch.spec.whatwg.org), and
804 // along with that potentially also specifying a setting for its
805 // 'authentication flag' to control how user:pass embedded in a
806 // media resource URL should be treated, then update the handling
807 // here to match.
808 KURL requestURL = m_currentSrc;
809 if (!requestURL.user().isEmpty())
810 requestURL.setUser(String());
811 if (!requestURL.pass().isEmpty())
812 requestURL.setPass(String());
813
814 m_player->load(loadType(), requestURL, corsMode());
815 }
816
817 void HTMLMediaElement::setPlayerPreload()
818 {
819 m_player->setPreload(m_preload);
820
821 if (loadIsDeferred() && m_preload != MediaPlayer::None)
822 startDeferredLoad();
823 }
824
825 bool HTMLMediaElement::loadIsDeferred() const
826 {
827 return m_deferredLoadState != NotDeferred;
828 }
829
830 void HTMLMediaElement::deferLoad()
831 {
832 // This implements the "optional" step 3 from the resource fetch algorithm.
833 ASSERT(!m_deferredLoadTimer.isActive());
834 ASSERT(m_deferredLoadState == NotDeferred);
835 // 1. Set the networkState to NETWORK_IDLE.
836 // 2. Queue a task to fire a simple event named suspend at the element.
837 changeNetworkStateFromLoadingToIdle();
838 // 3. Queue a task to set the element's delaying-the-load-event
839 // flag to false. This stops delaying the load event.
840 m_deferredLoadTimer.startOneShot(0, FROM_HERE);
841 // 4. Wait for the task to be run.
842 m_deferredLoadState = WaitingForStopDelayingLoadEventTask;
843 // Continued in executeDeferredLoad().
844 }
845
846 void HTMLMediaElement::cancelDeferredLoad()
847 {
848 m_deferredLoadTimer.stop();
849 m_deferredLoadState = NotDeferred;
850 }
851
852 void HTMLMediaElement::executeDeferredLoad()
853 {
854 ASSERT(m_deferredLoadState >= WaitingForTrigger);
855
856 // resource fetch algorithm step 3 - continued from deferLoad().
857
858 // 5. Wait for an implementation-defined event (e.g. the user requesting tha t the media element begin playback).
859 // This is assumed to be whatever 'event' ended up calling this method.
860 cancelDeferredLoad();
861 // 6. Set the element's delaying-the-load-event flag back to true (this
862 // delays the load event again, in case it hasn't been fired yet).
863 setShouldDelayLoadEvent(true);
864 // 7. Set the networkState to NETWORK_LOADING.
865 m_networkState = NETWORK_LOADING;
866
867 startProgressEventTimer();
868
869 startPlayerLoad();
870 }
871
872 void HTMLMediaElement::startDeferredLoad()
873 {
874 if (m_deferredLoadState == WaitingForTrigger) {
875 executeDeferredLoad();
876 return;
877 }
878 ASSERT(m_deferredLoadState == WaitingForStopDelayingLoadEventTask);
879 m_deferredLoadState = ExecuteOnStopDelayingLoadEventTask;
880 }
881
882 void HTMLMediaElement::deferredLoadTimerFired(Timer<HTMLMediaElement>*)
883 {
884 setShouldDelayLoadEvent(false);
885
886 if (m_deferredLoadState == ExecuteOnStopDelayingLoadEventTask) {
887 executeDeferredLoad();
888 return;
889 }
890 ASSERT(m_deferredLoadState == WaitingForStopDelayingLoadEventTask);
891 m_deferredLoadState = WaitingForTrigger;
892 }
893
894 WebMediaPlayer::LoadType HTMLMediaElement::loadType() const
895 {
896 if (m_mediaSource)
897 return WebMediaPlayer::LoadTypeMediaSource;
898
899 return WebMediaPlayer::LoadTypeURL;
900 }
901
902 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionI fInvalid)
903 {
904 if (!url.isValid()) {
905 WTF_LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because u rl is invalid", urlForLoggingMedia(url).utf8().data());
906 return false;
907 }
908
909 return true;
910 }
911
912 void HTMLMediaElement::startProgressEventTimer()
913 {
914 if (m_progressEventTimer.isActive())
915 return;
916
917 m_previousProgressTime = WTF::currentTime();
918 // 350ms is not magic, it is in the spec!
919 m_progressEventTimer.startRepeating(0.350, FROM_HERE);
920 }
921
922 void HTMLMediaElement::waitForSourceChange()
923 {
924 WTF_LOG(Media, "HTMLMediaElement::waitForSourceChange");
925
926 stopPeriodicTimers();
927 m_loadState = WaitingForSource;
928
929 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_N O_SOURCE value
930 m_networkState = NETWORK_NO_SOURCE;
931
932 // 6.18 - Set the element's delaying-the-load-event flag to false. This stop s delaying the load event.
933 setShouldDelayLoadEvent(false);
934
935 updateDisplayState();
936
937 if (renderer())
938 renderer()->updateFromElement();
939 }
940
941 void HTMLMediaElement::noneSupported()
942 {
943 WTF_LOG(Media, "HTMLMediaElement::noneSupported");
944
945 stopPeriodicTimers();
946 m_loadState = WaitingForSource;
947 m_currentSourceNode = nullptr;
948
949 // 4.8.10.5
950 // 6 - Reaching this step indicates that the media resource failed to load o r that the given
951 // URL could not be resolved. In one atomic operation, run the following ste ps:
952
953 // 6.1 - Set the error attribute to a new MediaError object whose code attri bute is set to
954 // MEDIA_ERR_SRC_NOT_SUPPORTED.
955 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
956
957 // 6.2 - Forget the media element's media-resource-specific text tracks.
958 // FIXME(sky): We have no tracks.
959
960 // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE v alue.
961 m_networkState = NETWORK_NO_SOURCE;
962
963 // 7 - Queue a task to fire a simple event named error at the media element.
964 scheduleEvent(EventTypeNames::error);
965
966 closeMediaSource();
967
968 // 8 - Set the element's delaying-the-load-event flag to false. This stops d elaying the load event.
969 setShouldDelayLoadEvent(false);
970
971 // 9 - Abort these steps. Until the load() method is invoked or the src attr ibute is changed,
972 // the element won't attempt to load another resource.
973
974 updateDisplayState();
975
976 if (renderer())
977 renderer()->updateFromElement();
978 }
979
980 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
981 {
982 ASSERT(m_readyState >= HAVE_METADATA);
983 WTF_LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(er r->code()));
984
985 // 1 - The user agent should cancel the fetching process.
986 stopPeriodicTimers();
987 m_loadState = WaitingForSource;
988
989 // 2 - Set the error attribute to a new MediaError object whose code attribu te is
990 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
991 m_error = err;
992
993 // 3 - Queue a task to fire a simple event named error at the media element.
994 scheduleEvent(EventTypeNames::error);
995
996 // 4 - Set the element's networkState attribute to the NETWORK_IDLE value.
997 m_networkState = NETWORK_IDLE;
998
999 // 5 - Set the element's delaying-the-load-event flag to false. This stops d elaying the load event.
1000 setShouldDelayLoadEvent(false);
1001
1002 // 6 - Abort the overall resource selection algorithm.
1003 m_currentSourceNode = nullptr;
1004 }
1005
1006 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1007 {
1008 WTF_LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
1009 m_asyncEventQueue->cancelAllEvents();
1010
1011 for (HTMLSourceElement* source = Traversal<HTMLSourceElement>::firstChild(*t his); source; source = Traversal<HTMLSourceElement>::nextSibling(*source))
1012 source->cancelPendingErrorEvent();
1013 }
1014
1015 void HTMLMediaElement::mediaPlayerNetworkStateChanged()
1016 {
1017 setNetworkState(webMediaPlayer()->networkState());
1018 }
1019
1020 void HTMLMediaElement::mediaLoadingFailed(WebMediaPlayer::NetworkState error)
1021 {
1022 stopPeriodicTimers();
1023
1024 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
1025 // <source> children, schedule the next one
1026 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
1027
1028 // resource selection algorithm
1029 // Step 9.Otherwise.9 - Failed with elements: Queue a task, using the DO M manipulation task source, to fire a simple event named error at the candidate element.
1030 if (m_currentSourceNode)
1031 m_currentSourceNode->scheduleErrorEvent();
1032 else
1033 WTF_LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
1034
1035 // 9.Otherwise.10 - Asynchronously await a stable state. The synchronous section consists of all the remaining steps of this algorithm until the algorit hm says the synchronous section has ended.
1036
1037 // 9.Otherwise.11 - Forget the media element's media-resource-specific t racks.
1038 // FIXME(sky): We have no tracks.
1039
1040 if (havePotentialSourceChild()) {
1041 WTF_LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
1042 scheduleNextSourceChild();
1043 } else {
1044 WTF_LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
1045 waitForSourceChange();
1046 }
1047
1048 return;
1049 }
1050
1051 if (error == WebMediaPlayer::NetworkStateNetworkError && m_readyState >= HAV E_METADATA)
1052 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
1053 else if (error == WebMediaPlayer::NetworkStateDecodeError)
1054 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
1055 else if ((error == WebMediaPlayer::NetworkStateFormatError
1056 || error == WebMediaPlayer::NetworkStateNetworkError)
1057 && m_loadState == LoadingFromSrcAttr)
1058 noneSupported();
1059
1060 updateDisplayState();
1061 }
1062
1063 void HTMLMediaElement::setNetworkState(WebMediaPlayer::NetworkState state)
1064 {
1065 WTF_LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d" , static_cast<int>(state), static_cast<int>(m_networkState));
1066
1067 if (state == WebMediaPlayer::NetworkStateEmpty) {
1068 // Just update the cached state and leave, we can't do anything.
1069 m_networkState = NETWORK_EMPTY;
1070 return;
1071 }
1072
1073 if (state == WebMediaPlayer::NetworkStateFormatError
1074 || state == WebMediaPlayer::NetworkStateNetworkError
1075 || state == WebMediaPlayer::NetworkStateDecodeError) {
1076 mediaLoadingFailed(state);
1077 return;
1078 }
1079
1080 if (state == WebMediaPlayer::NetworkStateIdle) {
1081 if (m_networkState > NETWORK_IDLE) {
1082 changeNetworkStateFromLoadingToIdle();
1083 setShouldDelayLoadEvent(false);
1084 } else {
1085 m_networkState = NETWORK_IDLE;
1086 }
1087 }
1088
1089 if (state == WebMediaPlayer::NetworkStateLoading) {
1090 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOU RCE)
1091 startProgressEventTimer();
1092 m_networkState = NETWORK_LOADING;
1093 }
1094
1095 if (state == WebMediaPlayer::NetworkStateLoaded) {
1096 if (m_networkState != NETWORK_IDLE)
1097 changeNetworkStateFromLoadingToIdle();
1098 m_completelyLoaded = true;
1099 }
1100 }
1101
1102 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
1103 {
1104 ASSERT(m_player);
1105 m_progressEventTimer.stop();
1106
1107 // Schedule one last progress event so we guarantee that at least one is fir ed
1108 // for files that load very quickly.
1109 if (webMediaPlayer() && webMediaPlayer()->didLoadingProgress())
1110 scheduleEvent(EventTypeNames::progress);
1111 scheduleEvent(EventTypeNames::suspend);
1112 m_networkState = NETWORK_IDLE;
1113 }
1114
1115 void HTMLMediaElement::mediaPlayerReadyStateChanged()
1116 {
1117 setReadyState(static_cast<ReadyState>(webMediaPlayer()->readyState()));
1118 }
1119
1120 void HTMLMediaElement::setReadyState(ReadyState state)
1121 {
1122 WTF_LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
1123
1124 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlay ing() uses it
1125 bool wasPotentiallyPlaying = potentiallyPlaying();
1126
1127 ReadyState oldState = m_readyState;
1128 ReadyState newState = state;
1129
1130 if (newState == oldState)
1131 return;
1132
1133 m_readyState = newState;
1134
1135 if (oldState > m_readyStateMaximum)
1136 m_readyStateMaximum = oldState;
1137
1138 if (m_networkState == NETWORK_EMPTY)
1139 return;
1140
1141 if (m_seeking) {
1142 // 4.8.10.9, step 9 note: If the media element was potentially playing i mmediately before
1143 // it started seeking, but seeking caused its readyState attribute to ch ange to a value
1144 // lower than HAVE_FUTURE_DATA, then a waiting will be fired at the elem ent.
1145 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
1146 scheduleEvent(EventTypeNames::waiting);
1147
1148 // 4.8.10.9 steps 12-14
1149 if (m_readyState >= HAVE_CURRENT_DATA)
1150 finishSeek();
1151 } else {
1152 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
1153 // 4.8.10.8
1154 scheduleTimeupdateEvent(false);
1155 scheduleEvent(EventTypeNames::waiting);
1156 }
1157 }
1158
1159 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
1160 prepareMediaFragmentURI();
1161
1162 m_duration = duration();
1163 scheduleEvent(EventTypeNames::durationchange);
1164 scheduleEvent(EventTypeNames::loadedmetadata);
1165 if (renderer())
1166 renderer()->updateFromElement();
1167 }
1168
1169 bool shouldUpdateDisplayState = false;
1170
1171 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_ haveFiredLoadedData) {
1172 m_haveFiredLoadedData = true;
1173 shouldUpdateDisplayState = true;
1174 scheduleEvent(EventTypeNames::loadeddata);
1175 setShouldDelayLoadEvent(false);
1176 applyMediaFragmentURI();
1177 }
1178
1179 bool isPotentiallyPlaying = potentiallyPlaying();
1180 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
1181 scheduleEvent(EventTypeNames::canplay);
1182 if (isPotentiallyPlaying)
1183 scheduleEvent(EventTypeNames::playing);
1184 shouldUpdateDisplayState = true;
1185 }
1186
1187 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
1188 if (oldState <= HAVE_CURRENT_DATA) {
1189 scheduleEvent(EventTypeNames::canplay);
1190 if (isPotentiallyPlaying)
1191 scheduleEvent(EventTypeNames::playing);
1192 }
1193
1194 if (m_autoplaying && m_paused && autoplay() && !m_userGestureRequiredFor Play) {
1195 m_paused = false;
1196 invalidateCachedTime();
1197 scheduleEvent(EventTypeNames::play);
1198 scheduleEvent(EventTypeNames::playing);
1199 }
1200
1201 scheduleEvent(EventTypeNames::canplaythrough);
1202
1203 shouldUpdateDisplayState = true;
1204 }
1205
1206 if (shouldUpdateDisplayState)
1207 updateDisplayState();
1208
1209 updatePlayState();
1210 }
1211
1212 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
1213 {
1214 ASSERT(m_player);
1215 if (m_networkState != NETWORK_LOADING)
1216 return;
1217
1218 double time = WTF::currentTime();
1219 double timedelta = time - m_previousProgressTime;
1220
1221 if (webMediaPlayer() && webMediaPlayer()->didLoadingProgress()) {
1222 scheduleEvent(EventTypeNames::progress);
1223 m_previousProgressTime = time;
1224 m_sentStalledEvent = false;
1225 if (renderer())
1226 renderer()->updateFromElement();
1227 } else if (timedelta > 3.0 && !m_sentStalledEvent) {
1228 scheduleEvent(EventTypeNames::stalled);
1229 m_sentStalledEvent = true;
1230 setShouldDelayLoadEvent(false);
1231 }
1232 }
1233
1234 void HTMLMediaElement::addPlayedRange(double start, double end)
1235 {
1236 WTF_LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
1237 if (!m_playedTimeRanges)
1238 m_playedTimeRanges = TimeRanges::create();
1239 m_playedTimeRanges->add(start, end);
1240 }
1241
1242 bool HTMLMediaElement::supportsSave() const
1243 {
1244 return webMediaPlayer() && webMediaPlayer()->supportsSave();
1245 }
1246
1247 void HTMLMediaElement::prepareToPlay()
1248 {
1249 WTF_LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
1250 if (m_havePreparedToPlay)
1251 return;
1252 m_havePreparedToPlay = true;
1253
1254 if (loadIsDeferred())
1255 startDeferredLoad();
1256 }
1257
1258 void HTMLMediaElement::seek(double time, ExceptionState& exceptionState)
1259 {
1260 WTF_LOG(Media, "HTMLMediaElement::seek(%f)", time);
1261
1262 // 4.8.10.9 Seeking
1263
1264 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an Inva lidStateError exception.
1265 if (m_readyState == HAVE_NOTHING) {
1266 exceptionState.throwDOMException(InvalidStateError, "The element's ready State is HAVE_NOTHING.");
1267 return;
1268 }
1269
1270 // If the media engine has been told to postpone loading data, let it go ahe ad now.
1271 if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
1272 prepareToPlay();
1273
1274 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
1275 refreshCachedTime();
1276 double now = currentTime();
1277
1278 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
1279 // already running. Abort that other instance of the algorithm without waiti ng for the step that
1280 // it is running to complete.
1281 // Nothing specific to be done here.
1282
1283 // 3 - Set the seeking IDL attribute to true.
1284 // The flag will be cleared when the engine tells us the time has actually c hanged.
1285 bool previousSeekStillPending = m_seeking;
1286 m_seeking = true;
1287
1288 // 5 - If the new playback position is later than the end of the media resou rce, then let it be the end
1289 // of the media resource instead.
1290 time = std::min(time, duration());
1291
1292 // 6 - If the new playback position is less than the earliest possible posit ion, let it be that position instead.
1293 time = std::max(time, 0.0);
1294
1295 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
1296 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
1297 // time scale, we will ask the media engine to "seek" to the current movie t ime, which may be a noop and
1298 // not generate a timechanged callback. This means m_seeking will never be c leared and we will never
1299 // fire a 'seeked' event.
1300 double mediaTime = webMediaPlayer()->mediaTimeForTimeValue(time);
1301 if (time != mediaTime) {
1302 WTF_LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent i s %f", time, mediaTime);
1303 time = mediaTime;
1304 }
1305
1306 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
1307 // seekable attribute, then let it be the position in one of the ranges give n in the seekable attribute
1308 // that is the nearest to the new playback position. ... If there are no ran ges given in the seekable
1309 // attribute then set the seeking IDL attribute to false and abort these ste ps.
1310 RefPtr<TimeRanges> seekableRanges = seekable();
1311
1312 // Short circuit seeking to the current time by just firing the events if no seek is required.
1313 // Don't skip calling the media engine if we are in poster mode because a se ek should always
1314 // cancel poster display.
1315 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMo de() != Poster);
1316
1317 if (noSeekRequired) {
1318 if (time == now) {
1319 scheduleEvent(EventTypeNames::seeking);
1320 if (previousSeekStillPending)
1321 return;
1322 // FIXME: There must be a stable state before timeupdate+seeked are dispatched and seeking
1323 // is reset to false. See http://crbug.com/266631
1324 scheduleTimeupdateEvent(false);
1325 scheduleEvent(EventTypeNames::seeked);
1326 }
1327 m_seeking = false;
1328 return;
1329 }
1330 time = seekableRanges->nearest(time);
1331
1332 if (m_playing) {
1333 if (m_lastSeekTime < now)
1334 addPlayedRange(m_lastSeekTime, now);
1335 }
1336 m_lastSeekTime = time;
1337 m_sentEndEvent = false;
1338
1339 // 8 - Queue a task to fire a simple event named seeking at the element.
1340 scheduleEvent(EventTypeNames::seeking);
1341
1342 // 9 - Set the current playback position to the given new playback position
1343 webMediaPlayer()->seek(time);
1344
1345 // 10-14 are handled, if necessary, when the engine signals a readystate cha nge or otherwise
1346 // satisfies seek completion and signals a time change.
1347 }
1348
1349 void HTMLMediaElement::finishSeek()
1350 {
1351 WTF_LOG(Media, "HTMLMediaElement::finishSeek");
1352
1353 // 4.8.10.9 Seeking completion
1354 // 12 - Set the seeking IDL attribute to false.
1355 m_seeking = false;
1356
1357 // 13 - Queue a task to fire a simple event named timeupdate at the element.
1358 scheduleTimeupdateEvent(false);
1359
1360 // 14 - Queue a task to fire a simple event named seeked at the element.
1361 scheduleEvent(EventTypeNames::seeked);
1362
1363 setDisplayMode(Video);
1364 }
1365
1366 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
1367 {
1368 return m_readyState;
1369 }
1370
1371 bool HTMLMediaElement::hasAudio() const
1372 {
1373 return webMediaPlayer() && webMediaPlayer()->hasAudio();
1374 }
1375
1376 bool HTMLMediaElement::seeking() const
1377 {
1378 return m_seeking;
1379 }
1380
1381 void HTMLMediaElement::refreshCachedTime() const
1382 {
1383 if (!webMediaPlayer() || m_readyState < HAVE_METADATA)
1384 return;
1385
1386 m_cachedTime = webMediaPlayer()->currentTime();
1387 }
1388
1389 void HTMLMediaElement::invalidateCachedTime()
1390 {
1391 WTF_LOG(Media, "HTMLMediaElement::invalidateCachedTime");
1392 m_cachedTime = MediaPlayer::invalidTime();
1393 }
1394
1395 // playback state
1396 double HTMLMediaElement::currentTime() const
1397 {
1398 if (m_readyState == HAVE_NOTHING)
1399 return 0;
1400
1401 if (m_seeking) {
1402 WTF_LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
1403 return m_lastSeekTime;
1404 }
1405
1406 if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) {
1407 #if LOG_CACHED_TIME_WARNINGS
1408 static const double minCachedDeltaForWarning = 0.01;
1409 double delta = m_cachedTime - webMediaPlayer()->currentTime();
1410 if (delta > minCachedDeltaForWarning)
1411 WTF_LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
1412 #endif
1413 return m_cachedTime;
1414 }
1415
1416 refreshCachedTime();
1417
1418 return m_cachedTime;
1419 }
1420
1421 void HTMLMediaElement::setCurrentTime(double time, ExceptionState& exceptionStat e)
1422 {
1423 seek(time, exceptionState);
1424 }
1425
1426 double HTMLMediaElement::duration() const
1427 {
1428 // FIXME: remove m_player check once we figure out how m_player is going
1429 // out of sync with readystate. m_player is cleared but readystate is not se t
1430 // to HAVE_NOTHING
1431 if (!m_player || m_readyState < HAVE_METADATA)
1432 return std::numeric_limits<double>::quiet_NaN();
1433
1434 // FIXME: Refactor so m_duration is kept current (in both MSE and
1435 // non-MSE cases) once we have transitioned from HAVE_NOTHING ->
1436 // HAVE_METADATA. Currently, m_duration may be out of date for at least MSE
1437 // case because MediaSource and SourceBuffer do not notify the element
1438 // directly upon duration changes caused by endOfStream, remove, or append
1439 // operations; rather the notification is triggered by the WebMediaPlayer
1440 // implementation observing that the underlying engine has updated duration
1441 // and notifying the element to consult its MediaSource for current
1442 // duration. See http://crbug.com/266644
1443
1444 if (m_mediaSource)
1445 return m_mediaSource->duration();
1446
1447 return webMediaPlayer()->duration();
1448 }
1449
1450 bool HTMLMediaElement::paused() const
1451 {
1452 return m_paused;
1453 }
1454
1455 double HTMLMediaElement::defaultPlaybackRate() const
1456 {
1457 return m_defaultPlaybackRate;
1458 }
1459
1460 void HTMLMediaElement::setDefaultPlaybackRate(double rate)
1461 {
1462 if (m_defaultPlaybackRate == rate)
1463 return;
1464
1465 m_defaultPlaybackRate = rate;
1466 scheduleEvent(EventTypeNames::ratechange);
1467 }
1468
1469 double HTMLMediaElement::playbackRate() const
1470 {
1471 return m_playbackRate;
1472 }
1473
1474 void HTMLMediaElement::setPlaybackRate(double rate)
1475 {
1476 WTF_LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
1477
1478 if (m_playbackRate != rate) {
1479 m_playbackRate = rate;
1480 invalidateCachedTime();
1481 scheduleEvent(EventTypeNames::ratechange);
1482 }
1483
1484 updatePlaybackRate();
1485 }
1486
1487 double HTMLMediaElement::effectivePlaybackRate() const
1488 {
1489 return m_playbackRate;
1490 }
1491
1492 HTMLMediaElement::DirectionOfPlayback HTMLMediaElement::directionOfPlayback() co nst
1493 {
1494 return m_playbackRate >= 0 ? Forward : Backward;
1495 }
1496
1497 void HTMLMediaElement::updatePlaybackRate()
1498 {
1499 double effectiveRate = effectivePlaybackRate();
1500 if (m_player && potentiallyPlaying())
1501 webMediaPlayer()->setRate(effectiveRate);
1502 }
1503
1504 bool HTMLMediaElement::ended() const
1505 {
1506 // 4.8.10.8 Playing the media resource
1507 // The ended attribute must return true if the media element has ended
1508 // playback and the direction of playback is forwards, and false otherwise.
1509 return endedPlayback() && directionOfPlayback() == Forward;
1510 }
1511
1512 bool HTMLMediaElement::autoplay() const
1513 {
1514 return hasAttribute(HTMLNames::autoplayAttr);
1515 }
1516
1517 String HTMLMediaElement::preload() const
1518 {
1519 switch (m_preload) {
1520 case MediaPlayer::None:
1521 return "none";
1522 break;
1523 case MediaPlayer::MetaData:
1524 return "metadata";
1525 break;
1526 case MediaPlayer::Auto:
1527 return "auto";
1528 break;
1529 }
1530
1531 ASSERT_NOT_REACHED();
1532 return String();
1533 }
1534
1535 void HTMLMediaElement::setPreload(const AtomicString& preload)
1536 {
1537 WTF_LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
1538 setAttribute(HTMLNames::preloadAttr, preload);
1539 }
1540
1541 void HTMLMediaElement::play()
1542 {
1543 WTF_LOG(Media, "HTMLMediaElement::play()");
1544
1545 if (m_userGestureRequiredForPlay && !UserGestureIndicator::processingUserGes ture())
1546 return;
1547 if (UserGestureIndicator::processingUserGesture())
1548 m_userGestureRequiredForPlay = false;
1549
1550 playInternal();
1551 }
1552
1553 void HTMLMediaElement::playInternal()
1554 {
1555 WTF_LOG(Media, "HTMLMediaElement::playInternal");
1556
1557 // 4.8.10.9. Playing the media resource
1558 if (!m_player || m_networkState == NETWORK_EMPTY)
1559 scheduleDelayedAction(LoadMediaResource);
1560
1561 if (endedPlayback())
1562 seek(0, IGNORE_EXCEPTION);
1563
1564 if (m_paused) {
1565 m_paused = false;
1566 invalidateCachedTime();
1567 scheduleEvent(EventTypeNames::play);
1568
1569 if (m_readyState <= HAVE_CURRENT_DATA)
1570 scheduleEvent(EventTypeNames::waiting);
1571 else if (m_readyState >= HAVE_FUTURE_DATA)
1572 scheduleEvent(EventTypeNames::playing);
1573 }
1574 m_autoplaying = false;
1575
1576 updatePlayState();
1577 }
1578
1579 void HTMLMediaElement::pause()
1580 {
1581 WTF_LOG(Media, "HTMLMediaElement::pause()");
1582
1583 if (!m_player || m_networkState == NETWORK_EMPTY)
1584 scheduleDelayedAction(LoadMediaResource);
1585
1586 m_autoplaying = false;
1587
1588 if (!m_paused) {
1589 m_paused = true;
1590 scheduleTimeupdateEvent(false);
1591 scheduleEvent(EventTypeNames::pause);
1592 }
1593
1594 updatePlayState();
1595 }
1596
1597 void HTMLMediaElement::closeMediaSource()
1598 {
1599 if (!m_mediaSource)
1600 return;
1601
1602 m_mediaSource->close();
1603 m_mediaSource = nullptr;
1604 }
1605
1606 bool HTMLMediaElement::loop() const
1607 {
1608 return hasAttribute(HTMLNames::loopAttr);
1609 }
1610
1611 void HTMLMediaElement::setLoop(bool b)
1612 {
1613 WTF_LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
1614 setBooleanAttribute(HTMLNames::loopAttr, b);
1615 }
1616
1617 double HTMLMediaElement::volume() const
1618 {
1619 return m_volume;
1620 }
1621
1622 void HTMLMediaElement::setVolume(double vol, ExceptionState& exceptionState)
1623 {
1624 WTF_LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
1625
1626 if (m_volume == vol)
1627 return;
1628
1629 if (vol < 0.0f || vol > 1.0f) {
1630 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::inde xOutsideRange("volume", vol, 0.0, ExceptionMessages::InclusiveBound, 1.0, Except ionMessages::InclusiveBound));
1631 return;
1632 }
1633
1634 m_volume = vol;
1635 updateVolume();
1636 scheduleEvent(EventTypeNames::volumechange);
1637 }
1638
1639 bool HTMLMediaElement::muted() const
1640 {
1641 return m_muted;
1642 }
1643
1644 void HTMLMediaElement::setMuted(bool muted)
1645 {
1646 WTF_LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
1647
1648 if (m_muted == muted)
1649 return;
1650
1651 m_muted = muted;
1652
1653 updateVolume();
1654
1655 scheduleEvent(EventTypeNames::volumechange);
1656 }
1657
1658 void HTMLMediaElement::updateVolume()
1659 {
1660 if (webMediaPlayer())
1661 webMediaPlayer()->setVolume(effectiveMediaVolume());
1662 }
1663
1664 double HTMLMediaElement::effectiveMediaVolume() const
1665 {
1666 if (m_muted)
1667 return 0;
1668
1669 return m_volume;
1670 }
1671
1672 // The spec says to fire periodic timeupdate events (those sent while playing) e very
1673 // "15 to 250ms", we choose the slowest frequency
1674 static const double maxTimeupdateEventFrequency = 0.25;
1675
1676 void HTMLMediaElement::startPlaybackProgressTimer()
1677 {
1678 if (m_playbackProgressTimer.isActive())
1679 return;
1680
1681 m_previousProgressTime = WTF::currentTime();
1682 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HER E);
1683 }
1684
1685 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
1686 {
1687 ASSERT(m_player);
1688
1689 if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fr agmentEndTime && directionOfPlayback() == Forward) {
1690 m_fragmentEndTime = MediaPlayer::invalidTime();
1691 if (!m_paused) {
1692 UseCounter::count(document(), UseCounter::HTMLMediaElementPauseAtFra gmentEnd);
1693 // changes paused to true and fires a simple event named pause at th e media element.
1694 pause();
1695 }
1696 }
1697
1698 if (!m_seeking)
1699 scheduleTimeupdateEvent(true);
1700 }
1701
1702 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
1703 {
1704 double now = WTF::currentTime();
1705 double timedelta = now - m_lastTimeUpdateEventWallTime;
1706
1707 // throttle the periodic events
1708 if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
1709 return;
1710
1711 // Some media engines make multiple "time changed" callbacks at the same tim e, but we only want one
1712 // event at a given time so filter here
1713 double movieTime = currentTime();
1714 if (movieTime != m_lastTimeUpdateEventMovieTime) {
1715 scheduleEvent(EventTypeNames::timeupdate);
1716 m_lastTimeUpdateEventWallTime = now;
1717 m_lastTimeUpdateEventMovieTime = movieTime;
1718 }
1719 }
1720
1721 bool HTMLMediaElement::togglePlayStateWillPlay() const
1722 {
1723 return paused();
1724 }
1725
1726 void HTMLMediaElement::togglePlayState()
1727 {
1728 if (paused())
1729 play();
1730 else
1731 pause();
1732 }
1733
1734 bool HTMLMediaElement::havePotentialSourceChild()
1735 {
1736 // Stash the current <source> node and next nodes so we can restore them aft er checking
1737 // to see there is another potential.
1738 RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
1739 RefPtr<Node> nextNode = m_nextChildNodeToConsider;
1740
1741 KURL nextURL = selectNextSourceChild(0, 0, DoNothing);
1742
1743 m_currentSourceNode = currentSourceNode;
1744 m_nextChildNodeToConsider = nextNode;
1745
1746 return nextURL.isValid();
1747 }
1748
1749 KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* k eySystem, InvalidURLAction actionIfInvalid)
1750 {
1751 #if !LOG_DISABLED
1752 // Don't log if this was just called to find out if there are any valid <sou rce> elements.
1753 bool shouldLog = actionIfInvalid != DoNothing;
1754 if (shouldLog)
1755 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild");
1756 #endif
1757
1758 if (!m_nextChildNodeToConsider) {
1759 #if !LOG_DISABLED
1760 if (shouldLog)
1761 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \ "\"");
1762 #endif
1763 return KURL();
1764 }
1765
1766 KURL mediaURL;
1767 Node* node;
1768 HTMLSourceElement* source = 0;
1769 String type;
1770 String system;
1771 bool lookingForStartNode = m_nextChildNodeToConsider;
1772 bool canUseSourceElement = false;
1773
1774 NodeVector potentialSourceNodes;
1775 getChildNodes(*this, potentialSourceNodes);
1776
1777 for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size() ; ++i) {
1778 node = potentialSourceNodes[i].get();
1779 if (lookingForStartNode && m_nextChildNodeToConsider != node)
1780 continue;
1781 lookingForStartNode = false;
1782
1783 if (!isHTMLSourceElement(*node))
1784 continue;
1785 if (node->parentNode() != this)
1786 continue;
1787
1788 source = toHTMLSourceElement(node);
1789
1790 // If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below
1791 mediaURL = source->getNonEmptyURLAttribute(HTMLNames::srcAttr);
1792 #if !LOG_DISABLED
1793 if (shouldLog)
1794 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is % s", urlForLoggingMedia(mediaURL).utf8().data());
1795 #endif
1796 if (mediaURL.isEmpty())
1797 goto check_again;
1798
1799 type = source->type();
1800 // FIXME(82965): Add support for keySystem in <source> and set system fr om source.
1801 if (type.isEmpty() && mediaURL.protocolIsData())
1802 type = mimeTypeFromDataURL(mediaURL);
1803 if (!type.isEmpty() || !system.isEmpty()) {
1804 #if !LOG_DISABLED
1805 if (shouldLog)
1806 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
1807 #endif
1808 if (!supportsType(ContentType(type), system))
1809 goto check_again;
1810 }
1811
1812 // Is it safe to load this url?
1813 if (!isSafeToLoadURL(mediaURL, actionIfInvalid))
1814 goto check_again;
1815
1816 // Making it this far means the <source> looks reasonable.
1817 canUseSourceElement = true;
1818
1819 check_again:
1820 if (!canUseSourceElement && actionIfInvalid == Complain && source)
1821 source->scheduleErrorEvent();
1822 }
1823
1824 if (canUseSourceElement) {
1825 if (contentType)
1826 *contentType = ContentType(type);
1827 if (keySystem)
1828 *keySystem = system;
1829 m_currentSourceNode = source;
1830 m_nextChildNodeToConsider = source->nextSibling();
1831 } else {
1832 m_currentSourceNode = nullptr;
1833 m_nextChildNodeToConsider = nullptr;
1834 }
1835
1836 #if !LOG_DISABLED
1837 if (shouldLog)
1838 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_cu rrentSourceNode.get(), canUseSourceElement ? urlForLoggingMedia(mediaURL).utf8() .data() : "");
1839 #endif
1840 return canUseSourceElement ? mediaURL : KURL();
1841 }
1842
1843 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
1844 {
1845 WTF_LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
1846
1847 #if !LOG_DISABLED
1848 KURL url = source->getNonEmptyURLAttribute(HTMLNames::srcAttr);
1849 WTF_LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLoggi ngMedia(url).utf8().data());
1850 #endif
1851
1852 // We should only consider a <source> element when there is not src attribut e at all.
1853 if (hasAttribute(HTMLNames::srcAttr))
1854 return;
1855
1856 // 4.8.8 - If a source element is inserted as a child of a media element tha t has no src
1857 // attribute and whose networkState has the value NETWORK_EMPTY, the user ag ent must invoke
1858 // the media element's resource selection algorithm.
1859 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
1860 scheduleDelayedAction(LoadMediaResource);
1861 m_nextChildNodeToConsider = source;
1862 return;
1863 }
1864
1865 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
1866 WTF_LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted imm ediately after current source");
1867 m_nextChildNodeToConsider = source;
1868 return;
1869 }
1870
1871 if (m_nextChildNodeToConsider)
1872 return;
1873
1874 if (m_loadState != WaitingForSource)
1875 return;
1876
1877 // 4.8.9.5, resource selection algorithm, source elements section:
1878 // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
1879 // 22. Asynchronously await a stable state...
1880 // 23. Set the element's delaying-the-load-event flag back to true (this del ays the load event again, in case
1881 // it hasn't been fired yet).
1882 setShouldDelayLoadEvent(true);
1883
1884 // 24. Set the networkState back to NETWORK_LOADING.
1885 m_networkState = NETWORK_LOADING;
1886
1887 // 25. Jump back to the find next candidate step above.
1888 m_nextChildNodeToConsider = source;
1889 scheduleNextSourceChild();
1890 }
1891
1892 void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source)
1893 {
1894 WTF_LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source);
1895
1896 #if !LOG_DISABLED
1897 KURL url = source->getNonEmptyURLAttribute(HTMLNames::srcAttr);
1898 WTF_LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLog gingMedia(url).utf8().data());
1899 #endif
1900
1901 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
1902 return;
1903
1904 if (source == m_nextChildNodeToConsider) {
1905 if (m_currentSourceNode)
1906 m_nextChildNodeToConsider = m_currentSourceNode->nextSibling();
1907 WTF_LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsi der set to %p", m_nextChildNodeToConsider.get());
1908 } else if (source == m_currentSourceNode) {
1909 // Clear the current source node pointer, but don't change the movie as the spec says:
1910 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
1911 // inserted in a video or audio element will have no effect.
1912 m_currentSourceNode = nullptr;
1913 WTF_LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode se t to 0");
1914 }
1915 }
1916
1917 void HTMLMediaElement::mediaPlayerTimeChanged()
1918 {
1919 WTF_LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
1920
1921 invalidateCachedTime();
1922
1923 // 4.8.10.9 steps 12-14. Needed if no ReadyState change is associated with t he seek.
1924 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA && !webMediaPlayer()->see king())
1925 finishSeek();
1926
1927 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
1928 // it will only queue a 'timeupdate' event if we haven't already posted one at the current
1929 // movie time.
1930 scheduleTimeupdateEvent(false);
1931
1932 double now = currentTime();
1933 double dur = duration();
1934
1935 // When the current playback position reaches the end of the media resource when the direction of
1936 // playback is forwards, then the user agent must follow these steps:
1937 if (!std::isnan(dur) && dur && now >= dur && directionOfPlayback() == Forwar d) {
1938 // If the media element has a loop attribute specified and does not have a current media controller,
1939 if (loop()) {
1940 m_sentEndEvent = false;
1941 // then seek to the earliest possible position of the media resourc e and abort these steps.
1942 seek(0, IGNORE_EXCEPTION);
1943 } else {
1944 // If the media element does not have a current media controller, an d the media element
1945 // has still ended playback, and the direction of playback is still forwards, and paused
1946 // is false,
1947 if (!m_paused) {
1948 // changes paused to true and fires a simple event named pause a t the media element.
1949 m_paused = true;
1950 scheduleEvent(EventTypeNames::pause);
1951 }
1952 // Queue a task to fire a simple event named ended at the media elem ent.
1953 if (!m_sentEndEvent) {
1954 m_sentEndEvent = true;
1955 scheduleEvent(EventTypeNames::ended);
1956 }
1957 }
1958 }
1959 else
1960 m_sentEndEvent = false;
1961
1962 updatePlayState();
1963 }
1964
1965 void HTMLMediaElement::mediaPlayerDurationChanged()
1966 {
1967 WTF_LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
1968 // FIXME: Change MediaPlayerClient & WebMediaPlayer to convey
1969 // the currentTime when the duration change occured. The current
1970 // WebMediaPlayer implementations always clamp currentTime() to
1971 // duration() so the requestSeek condition here is always false.
1972 durationChanged(duration(), currentTime() > duration());
1973 }
1974
1975 void HTMLMediaElement::durationChanged(double duration, bool requestSeek)
1976 {
1977 WTF_LOG(Media, "HTMLMediaElement::durationChanged(%f, %d)", duration, reques tSeek);
1978
1979 // Abort if duration unchanged.
1980 if (m_duration == duration)
1981 return;
1982
1983 WTF_LOG(Media, "HTMLMediaElement::durationChanged : %f -> %f", m_duration, d uration);
1984 m_duration = duration;
1985 scheduleEvent(EventTypeNames::durationchange);
1986
1987 if (renderer())
1988 renderer()->updateFromElement();
1989
1990 if (requestSeek)
1991 seek(duration, IGNORE_EXCEPTION);
1992 }
1993
1994 void HTMLMediaElement::mediaPlayerPlaybackStateChanged()
1995 {
1996 WTF_LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
1997
1998 if (!m_player || m_pausedInternal)
1999 return;
2000
2001 if (webMediaPlayer()->paused())
2002 pause();
2003 else
2004 playInternal();
2005 }
2006
2007 void HTMLMediaElement::mediaPlayerRequestFullscreen()
2008 {
2009 // FIXME(sky): How do we go full screen now?
2010 }
2011
2012 void HTMLMediaElement::mediaPlayerRequestSeek(double time)
2013 {
2014 setCurrentTime(time, IGNORE_EXCEPTION);
2015 }
2016
2017 // MediaPlayerPresentation methods
2018 void HTMLMediaElement::mediaPlayerRepaint()
2019 {
2020 if (m_webLayer)
2021 m_webLayer->invalidate();
2022
2023 updateDisplayState();
2024 if (renderer())
2025 renderer()->setShouldDoFullPaintInvalidation(true);
2026 }
2027
2028 void HTMLMediaElement::mediaPlayerSizeChanged()
2029 {
2030 WTF_LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
2031
2032 if (renderer())
2033 renderer()->updateFromElement();
2034 }
2035
2036 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
2037 {
2038 if (m_mediaSource)
2039 return m_mediaSource->buffered();
2040
2041 if (!webMediaPlayer())
2042 return TimeRanges::create();
2043
2044 return TimeRanges::create(webMediaPlayer()->buffered());
2045 }
2046
2047 PassRefPtr<TimeRanges> HTMLMediaElement::played()
2048 {
2049 if (m_playing) {
2050 double time = currentTime();
2051 if (time > m_lastSeekTime)
2052 addPlayedRange(m_lastSeekTime, time);
2053 }
2054
2055 if (!m_playedTimeRanges)
2056 m_playedTimeRanges = TimeRanges::create();
2057
2058 return m_playedTimeRanges->copy();
2059 }
2060
2061 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
2062 {
2063 if (webMediaPlayer()) {
2064 double maxTimeSeekable = webMediaPlayer()->maxTimeSeekable();
2065 if (maxTimeSeekable)
2066 return TimeRanges::create(0, maxTimeSeekable);
2067 }
2068 return TimeRanges::create();
2069 }
2070
2071 bool HTMLMediaElement::potentiallyPlaying() const
2072 {
2073 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
2074 // when it ran out of buffered data. A movie is this state is "potentially p laying", modulo the
2075 // checks in couldPlayIfEnoughData().
2076 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyStat e < HAVE_FUTURE_DATA;
2077 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEn oughData();
2078 }
2079
2080 bool HTMLMediaElement::couldPlayIfEnoughData() const
2081 {
2082 return !paused() && !endedPlayback() && !stoppedDueToErrors();
2083 }
2084
2085 bool HTMLMediaElement::endedPlayback() const
2086 {
2087 double dur = duration();
2088 if (!m_player || std::isnan(dur))
2089 return false;
2090
2091 // 4.8.10.8 Playing the media resource
2092
2093 // A media element is said to have ended playback when the element's
2094 // readyState attribute is HAVE_METADATA or greater,
2095 if (m_readyState < HAVE_METADATA)
2096 return false;
2097
2098 // and the current playback position is the end of the media resource and th e direction
2099 // of playback is forwards, Either the media element does not have a loop at tribute specified,
2100 // or the media element has a current media controller.
2101 double now = currentTime();
2102 if (directionOfPlayback() == Forward)
2103 return dur > 0 && now >= dur && !loop();
2104
2105 // or the current playback position is the earliest possible position and th e direction
2106 // of playback is backwards
2107 ASSERT(directionOfPlayback() == Backward);
2108 return now <= 0;
2109 }
2110
2111 bool HTMLMediaElement::stoppedDueToErrors() const
2112 {
2113 if (m_readyState >= HAVE_METADATA && m_error) {
2114 RefPtr<TimeRanges> seekableRanges = seekable();
2115 if (!seekableRanges->contain(currentTime()))
2116 return true;
2117 }
2118
2119 return false;
2120 }
2121
2122 void HTMLMediaElement::updatePlayState()
2123 {
2124 if (!m_player)
2125 return;
2126
2127 bool isPlaying = webMediaPlayer() && !webMediaPlayer()->paused();
2128 if (m_pausedInternal) {
2129 if (isPlaying)
2130 webMediaPlayer()->pause();
2131 refreshCachedTime();
2132 m_playbackProgressTimer.stop();
2133 return;
2134 }
2135
2136 bool shouldBePlaying = potentiallyPlaying();
2137
2138 WTF_LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, is Playing = %s",
2139 boolString(shouldBePlaying), boolString(isPlaying));
2140
2141 if (shouldBePlaying) {
2142 setDisplayMode(Video);
2143 invalidateCachedTime();
2144
2145 if (!isPlaying) {
2146 // Set rate, muted before calling play in case they were set before the media engine was setup.
2147 // The media engine should just stash the rate and muted values sinc e it isn't already playing.
2148 webMediaPlayer()->setRate(effectivePlaybackRate());
2149 updateVolume();
2150 webMediaPlayer()->play();
2151 }
2152
2153 startPlaybackProgressTimer();
2154 m_playing = true;
2155
2156 } else { // Should not be playing right now
2157 if (isPlaying)
2158 webMediaPlayer()->pause();
2159 refreshCachedTime();
2160
2161 m_playbackProgressTimer.stop();
2162 m_playing = false;
2163 double time = currentTime();
2164 if (time > m_lastSeekTime)
2165 addPlayedRange(m_lastSeekTime, time);
2166
2167 if (couldPlayIfEnoughData())
2168 prepareToPlay();
2169 }
2170
2171 if (renderer())
2172 renderer()->updateFromElement();
2173 }
2174
2175 void HTMLMediaElement::setPausedInternal(bool b)
2176 {
2177 m_pausedInternal = b;
2178 updatePlayState();
2179 }
2180
2181 void HTMLMediaElement::stopPeriodicTimers()
2182 {
2183 m_progressEventTimer.stop();
2184 m_playbackProgressTimer.stop();
2185 }
2186
2187 void HTMLMediaElement::userCancelledLoad()
2188 {
2189 WTF_LOG(Media, "HTMLMediaElement::userCancelledLoad");
2190
2191 // If the media data fetching process is aborted by the user:
2192
2193 // 1 - The user agent should cancel the fetching process.
2194 clearMediaPlayer(-1);
2195
2196 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
2197 return;
2198
2199 // 2 - Set the error attribute to a new MediaError object whose code attribu te is set to MEDIA_ERR_ABORTED.
2200 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
2201
2202 // 3 - Queue a task to fire a simple event named error at the media element.
2203 scheduleEvent(EventTypeNames::abort);
2204
2205 closeMediaSource();
2206
2207 // 4 - If the media element's readyState attribute has a value equal to HAVE _NOTHING, set the
2208 // element's networkState attribute to the NETWORK_EMPTY value and queue a t ask to fire a
2209 // simple event named emptied at the element. Otherwise, set the element's n etworkState
2210 // attribute to the NETWORK_IDLE value.
2211 if (m_readyState == HAVE_NOTHING) {
2212 m_networkState = NETWORK_EMPTY;
2213 scheduleEvent(EventTypeNames::emptied);
2214 }
2215 else
2216 m_networkState = NETWORK_IDLE;
2217
2218 // 5 - Set the element's delaying-the-load-event flag to false. This stops d elaying the load event.
2219 setShouldDelayLoadEvent(false);
2220
2221 // 6 - Abort the overall resource selection algorithm.
2222 m_currentSourceNode = nullptr;
2223
2224 // Reset m_readyState since m_player is gone.
2225 m_readyState = HAVE_NOTHING;
2226 invalidateCachedTime();
2227 }
2228
2229 void HTMLMediaElement::clearMediaPlayerAndAudioSourceProviderClientWithoutLockin g()
2230 {
2231 m_player.clear();
2232 }
2233
2234 void HTMLMediaElement::clearMediaPlayer(int flags)
2235 {
2236 closeMediaSource();
2237
2238 cancelDeferredLoad();
2239 clearMediaPlayerAndAudioSourceProviderClientWithoutLocking();
2240
2241 stopPeriodicTimers();
2242 m_loadTimer.stop();
2243
2244 m_pendingActionFlags &= ~flags;
2245 m_loadState = WaitingForSource;
2246 }
2247
2248 void HTMLMediaElement::stop()
2249 {
2250 WTF_LOG(Media, "HTMLMediaElement::stop");
2251
2252 m_active = false;
2253 userCancelledLoad();
2254
2255 // Stop the playback without generating events
2256 m_playing = false;
2257 setPausedInternal(true);
2258
2259 if (renderer())
2260 renderer()->updateFromElement();
2261
2262 stopPeriodicTimers();
2263 cancelPendingEventsAndCallbacks();
2264
2265 m_asyncEventQueue->close();
2266 }
2267
2268 bool HTMLMediaElement::hasPendingActivity() const
2269 {
2270 return (hasAudio() && isPlaying()) || m_asyncEventQueue->hasPendingEvents();
2271 }
2272
2273 void HTMLMediaElement::contextDestroyed()
2274 {
2275 ActiveDOMObject::contextDestroyed();
2276 }
2277
2278 bool HTMLMediaElement::isFullscreen() const
2279 {
2280 // FIXME(sky): How does video go full screen now?
2281 return false;
2282 }
2283
2284 void HTMLMediaElement::enterFullscreen()
2285 {
2286 }
2287
2288 void HTMLMediaElement::exitFullscreen()
2289 {
2290 }
2291
2292 blink::WebLayer* HTMLMediaElement::platformLayer() const
2293 {
2294 return m_webLayer;
2295 }
2296
2297 bool HTMLMediaElement::isURLAttribute(const Attribute& attribute) const
2298 {
2299 return attribute.name() == HTMLNames::srcAttr || HTMLElement::isURLAttribute (attribute);
2300 }
2301
2302 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
2303 {
2304 if (m_shouldDelayLoadEvent == shouldDelay)
2305 return;
2306
2307 WTF_LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(s houldDelay));
2308
2309 m_shouldDelayLoadEvent = shouldDelay;
2310 if (shouldDelay)
2311 document().incrementLoadEventDelayCount();
2312 else
2313 document().decrementLoadEventDelayCount();
2314 }
2315
2316 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
2317 {
2318 return 0;
2319 }
2320
2321 void HTMLMediaElement::createMediaPlayer()
2322 {
2323 closeMediaSource();
2324 m_player = MediaPlayer::create(this);
2325 }
2326
2327 const AtomicString& HTMLMediaElement::mediaGroup() const
2328 {
2329 return getAttribute(HTMLNames::mediagroupAttr);
2330 }
2331
2332 bool HTMLMediaElement::isBlocked() const
2333 {
2334 // A media element is a blocked media element if its readyState attribute is in the
2335 // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA sta te,
2336 // or if the element has paused for user interaction or paused for in-band c ontent.
2337 if (m_readyState <= HAVE_CURRENT_DATA)
2338 return true;
2339
2340 return false;
2341 }
2342
2343 void HTMLMediaElement::prepareMediaFragmentURI()
2344 {
2345 MediaFragmentURIParser fragmentParser(m_currentSrc);
2346 double dur = duration();
2347
2348 double start = fragmentParser.startTime();
2349 if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) {
2350 m_fragmentStartTime = start;
2351 if (m_fragmentStartTime > dur)
2352 m_fragmentStartTime = dur;
2353 } else
2354 m_fragmentStartTime = MediaPlayer::invalidTime();
2355
2356 double end = fragmentParser.endTime();
2357 if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_ fragmentStartTime) {
2358 m_fragmentEndTime = end;
2359 if (m_fragmentEndTime > dur)
2360 m_fragmentEndTime = dur;
2361 } else
2362 m_fragmentEndTime = MediaPlayer::invalidTime();
2363
2364 if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE _FUTURE_DATA)
2365 prepareToPlay();
2366 }
2367
2368 void HTMLMediaElement::applyMediaFragmentURI()
2369 {
2370 if (m_fragmentStartTime != MediaPlayer::invalidTime()) {
2371 m_sentEndEvent = false;
2372 UseCounter::count(document(), UseCounter::HTMLMediaElementSeekToFragment Start);
2373 seek(m_fragmentStartTime, IGNORE_EXCEPTION);
2374 }
2375 }
2376
2377 WebMediaPlayer::CORSMode HTMLMediaElement::corsMode() const
2378 {
2379 const AtomicString& crossOriginMode = getAttribute(HTMLNames::crossoriginAtt r);
2380 if (crossOriginMode.isNull())
2381 return WebMediaPlayer::CORSModeUnspecified;
2382 if (equalIgnoringCase(crossOriginMode, "use-credentials"))
2383 return WebMediaPlayer::CORSModeUseCredentials;
2384 return WebMediaPlayer::CORSModeAnonymous;
2385 }
2386
2387 void HTMLMediaElement::mediaPlayerSetWebLayer(blink::WebLayer* webLayer)
2388 {
2389 if (webLayer == m_webLayer)
2390 return;
2391
2392 // If either of the layers is null we need to enable or disable compositing. This is done by triggering a style recalc.
2393 if (!m_webLayer || !webLayer)
2394 setNeedsCompositingUpdate();
2395
2396 if (m_webLayer)
2397 GraphicsLayer::unregisterContentsLayer(m_webLayer);
2398 m_webLayer = webLayer;
2399 if (m_webLayer) {
2400 GraphicsLayer::registerContentsLayer(m_webLayer);
2401 }
2402 }
2403
2404 void HTMLMediaElement::mediaPlayerMediaSourceOpened(blink::WebMediaSource* webMe diaSource)
2405 {
2406 m_mediaSource->setWebMediaSourceAndOpen(adoptPtr(webMediaSource));
2407 }
2408
2409 void HTMLMediaElement::defaultEventHandler(Event* event)
2410 {
2411 HTMLElement::defaultEventHandler(event);
2412 }
2413
2414 }
OLDNEW
« no previous file with comments | « sky/engine/core/html/HTMLMediaElement.h ('k') | sky/engine/core/html/HTMLMediaElement.idl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698