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#if ENABLE(VIDEO) 28#include "HTMLMediaElement.h" 29 30#include "ApplicationCacheHost.h" 31#include "ApplicationCacheResource.h" 32#include "Attribute.h" 33#include "Chrome.h" 34#include "ChromeClient.h" 35#include "ClientRect.h" 36#include "ClientRectList.h" 37#include "ContentSecurityPolicy.h" 38#include "ContentType.h" 39#include "CSSPropertyNames.h" 40#include "CSSValueKeywords.h" 41#include "DiagnosticLoggingKeys.h" 42#include "DocumentLoader.h" 43#include "ElementShadow.h" 44#include "Event.h" 45#include "EventNames.h" 46#include "ExceptionCode.h" 47#include "ExceptionCodePlaceholder.h" 48#include "Frame.h" 49#include "FrameLoader.h" 50#include "FrameLoaderClient.h" 51#include "FrameView.h" 52#include "HTMLDocument.h" 53#include "HTMLNames.h" 54#include "HTMLSourceElement.h" 55#include "HTMLVideoElement.h" 56#include "Language.h" 57#include "Logging.h" 58#include "MediaController.h" 59#include "MediaControls.h" 60#include "MediaDocument.h" 61#include "MediaError.h" 62#include "MediaFragmentURIParser.h" 63#include "MediaKeyError.h" 64#include "MediaKeyEvent.h" 65#include "MediaList.h" 66#include "MediaPlayer.h" 67#include "MediaQueryEvaluator.h" 68#include "MouseEvent.h" 69#include "MIMETypeRegistry.h" 70#include "NodeRenderingContext.h" 71#include "Page.h" 72#include "PageActivityAssertionToken.h" 73#include "PageGroup.h" 74#include "RenderVideo.h" 75#include "RenderView.h" 76#include "ScriptController.h" 77#include "ScriptEventListener.h" 78#include "SecurityOrigin.h" 79#include "SecurityPolicy.h" 80#include "Settings.h" 81#include "ShadowRoot.h" 82#include "TimeRanges.h" 83#include <limits> 84#include <wtf/CurrentTime.h> 85#include <wtf/MathExtras.h> 86#include <wtf/NonCopyingSort.h> 87#include <wtf/Uint8Array.h> 88#include <wtf/text/CString.h> 89 90#if USE(ACCELERATED_COMPOSITING) 91#include "RenderLayerCompositor.h" 92#endif 93 94#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 95#include "RenderEmbeddedObject.h" 96#include "Widget.h" 97#endif 98 99#if ENABLE(VIDEO_TRACK) 100#include "AudioTrackList.h" 101#include "AudioTrackPrivate.h" 102#include "CaptionUserPreferences.h" 103#include "HTMLTrackElement.h" 104#include "InbandTextTrack.h" 105#include "InbandTextTrackPrivate.h" 106#include "RuntimeEnabledFeatures.h" 107#include "TextTrackCueList.h" 108#include "TextTrackList.h" 109#include "VideoTrackList.h" 110#include "VideoTrackPrivate.h" 111#endif 112 113#if ENABLE(WEB_AUDIO) 114#include "AudioSourceProvider.h" 115#include "MediaElementAudioSourceNode.h" 116#endif 117 118#if PLATFORM(MAC) 119#include "DisplaySleepDisabler.h" 120#endif 121 122#if ENABLE(MEDIA_SOURCE) 123#include "MediaSource.h" 124#include "MediaSourceRegistry.h" 125#endif 126 127#if ENABLE(MEDIA_STREAM) 128#include "MediaStreamRegistry.h" 129#endif 130 131#if ENABLE(ENCRYPTED_MEDIA_V2) 132#include "MediaKeyNeededEvent.h" 133#include "MediaKeys.h" 134#endif 135 136#if USE(PLATFORM_TEXT_TRACK_MENU) 137#include "PlatformTextTrack.h" 138#endif 139 140#if USE(AUDIO_SESSION) 141#include "AudioSessionManager.h" 142#endif 143 144using namespace std; 145 146namespace WebCore { 147 148static void setFlags(unsigned& value, unsigned flags) 149{ 150 value |= flags; 151} 152 153static void clearFlags(unsigned& value, unsigned flags) 154{ 155 value &= ~flags; 156} 157 158#if !LOG_DISABLED 159static String urlForLoggingMedia(const KURL& url) 160{ 161 static const unsigned maximumURLLengthForLogging = 128; 162 163 if (url.string().length() < maximumURLLengthForLogging) 164 return url.string(); 165 return url.string().substring(0, maximumURLLengthForLogging) + "..."; 166} 167 168static const char* boolString(bool val) 169{ 170 return val ? "true" : "false"; 171} 172#endif 173 174#ifndef LOG_MEDIA_EVENTS 175// Default to not logging events because so many are generated they can overwhelm the rest of 176// the logging. 177#define LOG_MEDIA_EVENTS 0 178#endif 179 180#ifndef LOG_CACHED_TIME_WARNINGS 181// Default to not logging warnings about excessive drift in the cached media time because it adds a 182// fair amount of overhead and logging. 183#define LOG_CACHED_TIME_WARNINGS 0 184#endif 185 186#if ENABLE(MEDIA_SOURCE) 187// URL protocol used to signal that the media source API is being used. 188static const char* mediaSourceBlobProtocol = "blob"; 189#endif 190 191using namespace HTMLNames; 192using namespace std; 193 194typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap; 195static DocumentElementSetMap& documentToElementSetMap() 196{ 197 DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ()); 198 return map; 199} 200 201static void addElementToDocumentMap(HTMLMediaElement* element, Document* document) 202{ 203 DocumentElementSetMap& map = documentToElementSetMap(); 204 HashSet<HTMLMediaElement*> set = map.take(document); 205 set.add(element); 206 map.add(document, set); 207} 208 209static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document) 210{ 211 DocumentElementSetMap& map = documentToElementSetMap(); 212 HashSet<HTMLMediaElement*> set = map.take(document); 213 set.remove(element); 214 if (!set.isEmpty()) 215 map.add(document, set); 216} 217 218#if ENABLE(ENCRYPTED_MEDIA) 219static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception) 220{ 221 switch (exception) { 222 case MediaPlayer::NoError: 223 return 0; 224 case MediaPlayer::InvalidPlayerState: 225 return INVALID_STATE_ERR; 226 case MediaPlayer::KeySystemNotSupported: 227 return NOT_SUPPORTED_ERR; 228 } 229 230 ASSERT_NOT_REACHED(); 231 return INVALID_STATE_ERR; 232} 233#endif 234 235#if ENABLE(VIDEO_TRACK) 236class TrackDisplayUpdateScope { 237public: 238 TrackDisplayUpdateScope(HTMLMediaElement* mediaElement) 239 { 240 m_mediaElement = mediaElement; 241 m_mediaElement->beginIgnoringTrackDisplayUpdateRequests(); 242 } 243 ~TrackDisplayUpdateScope() 244 { 245 ASSERT(m_mediaElement); 246 m_mediaElement->endIgnoringTrackDisplayUpdateRequests(); 247 } 248 249private: 250 HTMLMediaElement* m_mediaElement; 251}; 252#endif 253 254HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document, bool createdByParser) 255 : HTMLElement(tagName, document) 256 , ActiveDOMObject(document) 257 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) 258 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) 259 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired) 260 , m_playedTimeRanges() 261 , m_asyncEventQueue(GenericEventQueue::create(this)) 262 , m_playbackRate(1.0f) 263 , m_defaultPlaybackRate(1.0f) 264 , m_webkitPreservesPitch(true) 265 , m_networkState(NETWORK_EMPTY) 266 , m_readyState(HAVE_NOTHING) 267 , m_readyStateMaximum(HAVE_NOTHING) 268 , m_volume(1.0f) 269 , m_lastSeekTime(0) 270 , m_previousProgressTime(numeric_limits<double>::max()) 271 , m_lastTimeUpdateEventWallTime(0) 272 , m_lastTimeUpdateEventMovieTime(numeric_limits<double>::max()) 273 , m_loadState(WaitingForSource) 274#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 275 , m_proxyWidget(0) 276#endif 277 , m_restrictions(RequireUserGestureForFullscreenRestriction | RequirePageConsentToLoadMediaRestriction) 278 , m_preload(MediaPlayer::Auto) 279 , m_displayMode(Unknown) 280 , m_processingMediaPlayerCallback(0) 281 , m_cachedTime(MediaPlayer::invalidTime()) 282 , m_cachedTimeWallClockUpdateTime(0) 283 , m_minimumWallClockTimeToCacheMediaTime(0) 284 , m_fragmentStartTime(MediaPlayer::invalidTime()) 285 , m_fragmentEndTime(MediaPlayer::invalidTime()) 286 , m_pendingActionFlags(0) 287 , m_playing(false) 288 , m_isWaitingUntilMediaCanStart(false) 289 , m_shouldDelayLoadEvent(false) 290 , m_haveFiredLoadedData(false) 291 , m_inActiveDocument(true) 292 , m_autoplaying(true) 293 , m_muted(false) 294 , m_paused(true) 295 , m_seeking(false) 296 , m_sentStalledEvent(false) 297 , m_sentEndEvent(false) 298 , m_pausedInternal(false) 299 , m_sendProgressEvents(true) 300 , m_isFullscreen(false) 301 , m_closedCaptionsVisible(false) 302 , m_webkitLegacyClosedCaptionOverride(false) 303#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 304 , m_needWidgetUpdate(false) 305#endif 306 , m_dispatchingCanPlayEvent(false) 307 , m_loadInitiatedByUserGesture(false) 308 , m_completelyLoaded(false) 309 , m_havePreparedToPlay(false) 310 , m_parsingInProgress(createdByParser) 311#if ENABLE(VIDEO_TRACK) 312 , m_tracksAreReady(true) 313 , m_haveVisibleTextTrack(false) 314 , m_processingPreferenceChange(false) 315 , m_lastTextTrackUpdateTime(-1) 316 , m_captionDisplayMode(CaptionUserPreferences::Automatic) 317 , m_audioTracks(0) 318 , m_textTracks(0) 319 , m_videoTracks(0) 320 , m_ignoreTrackDisplayUpdate(0) 321#endif 322#if ENABLE(WEB_AUDIO) 323 , m_audioSourceNode(0) 324#endif 325#if USE(AUDIO_SESSION) 326 , m_audioSessionManagerToken(AudioSessionManagerToken::create(tagName == videoTag ? AudioSessionManager::Video : AudioSessionManager::Audio)) 327#endif 328 , m_reportedExtraMemoryCost(0) 329{ 330 LOG(Media, "HTMLMediaElement::HTMLMediaElement"); 331 332 if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture()) { 333 addBehaviorRestriction(RequireUserGestureForRateChangeRestriction); 334 addBehaviorRestriction(RequireUserGestureForLoadRestriction); 335 } 336 337 setHasCustomStyleCallbacks(); 338 339#if ENABLE(VIDEO_TRACK) 340 if (document->page()) 341 m_captionDisplayMode = document->page()->group().captionPreferences()->captionDisplayMode(); 342#endif 343 344 registerWithDocument(document); 345} 346 347HTMLMediaElement::~HTMLMediaElement() 348{ 349 LOG(Media, "HTMLMediaElement::~HTMLMediaElement"); 350 351 m_asyncEventQueue->close(); 352 353 setShouldDelayLoadEvent(false); 354 355 unregisterWithDocument(document()); 356 357#if ENABLE(VIDEO_TRACK) 358 if (m_audioTracks) { 359 m_audioTracks->clearElement(); 360 for (unsigned i = 0; i < m_audioTracks->length(); ++i) 361 m_audioTracks->item(i)->clearClient(); 362 } 363 if (m_textTracks) 364 m_textTracks->clearElement(); 365 if (m_textTracks) { 366 for (unsigned i = 0; i < m_textTracks->length(); ++i) 367 m_textTracks->item(i)->clearClient(); 368 } 369 if (m_videoTracks) { 370 m_videoTracks->clearElement(); 371 for (unsigned i = 0; i < m_videoTracks->length(); ++i) 372 m_videoTracks->item(i)->clearClient(); 373 } 374#endif 375 376 if (m_mediaController) { 377 m_mediaController->removeMediaElement(this); 378 m_mediaController = 0; 379 } 380 381#if ENABLE(MEDIA_SOURCE) 382 setSourceState(MediaSource::closedKeyword()); 383#endif 384 385#if ENABLE(ENCRYPTED_MEDIA_V2) 386 setMediaKeys(0); 387#endif 388 389 m_completelyLoaded = true; 390 if (m_player) 391 m_player->clearMediaPlayerClient(); 392} 393 394void HTMLMediaElement::registerWithDocument(Document* document) 395{ 396 if (m_isWaitingUntilMediaCanStart) 397 document->addMediaCanStartListener(this); 398 399 document->registerForMediaVolumeCallbacks(this); 400 document->registerForPrivateBrowsingStateChangedCallbacks(this); 401 402#if ENABLE(VIDEO_TRACK) 403 document->registerForCaptionPreferencesChangedCallbacks(this); 404#endif 405 406 addElementToDocumentMap(this, document); 407} 408 409void HTMLMediaElement::unregisterWithDocument(Document* document) 410{ 411 if (m_isWaitingUntilMediaCanStart) 412 document->removeMediaCanStartListener(this); 413 414 document->unregisterForMediaVolumeCallbacks(this); 415 document->unregisterForPrivateBrowsingStateChangedCallbacks(this); 416 417#if ENABLE(VIDEO_TRACK) 418 document->unregisterForCaptionPreferencesChangedCallbacks(this); 419#endif 420 421 removeElementFromDocumentMap(this, document); 422} 423 424void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument) 425{ 426 if (m_shouldDelayLoadEvent) { 427 if (oldDocument) 428 oldDocument->decrementLoadEventDelayCount(); 429 document()->incrementLoadEventDelayCount(); 430 } 431 432 if (oldDocument) { 433 unregisterWithDocument(oldDocument); 434 } 435 436 registerWithDocument(document()); 437 438 HTMLElement::didMoveToNewDocument(oldDocument); 439} 440 441bool HTMLMediaElement::hasCustomFocusLogic() const 442{ 443 return true; 444} 445 446bool HTMLMediaElement::supportsFocus() const 447{ 448 if (ownerDocument()->isMediaDocument()) 449 return false; 450 451 // If no controls specified, we should still be able to focus the element if it has tabIndex. 452 return controls() || HTMLElement::supportsFocus(); 453} 454 455bool HTMLMediaElement::isMouseFocusable() const 456{ 457 return false; 458} 459 460void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 461{ 462 if (name == srcAttr) { 463 // Trigger a reload, as long as the 'src' attribute is present. 464 if (!value.isNull()) { 465 clearMediaPlayer(LoadMediaResource); 466 scheduleDelayedAction(LoadMediaResource); 467 } 468 } else if (name == controlsAttr) 469 configureMediaControls(); 470#if PLATFORM(MAC) 471 else if (name == loopAttr) 472 updateDisableSleep(); 473#endif 474 else if (name == preloadAttr) { 475 if (equalIgnoringCase(value, "none")) 476 m_preload = MediaPlayer::None; 477 else if (equalIgnoringCase(value, "metadata")) 478 m_preload = MediaPlayer::MetaData; 479 else { 480 // The spec does not define an "invalid value default" but "auto" is suggested as the 481 // "missing value default", so use it for everything except "none" and "metadata" 482 m_preload = MediaPlayer::Auto; 483 } 484 485 // The attribute must be ignored if the autoplay attribute is present 486 if (!autoplay() && m_player) 487 m_player->setPreload(m_preload); 488 489 } else if (name == mediagroupAttr) 490 setMediaGroup(value); 491 else if (name == onabortAttr) 492 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, name, value)); 493 else if (name == onbeforeloadAttr) 494 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, name, value)); 495 else if (name == oncanplayAttr) 496 setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, name, value)); 497 else if (name == oncanplaythroughAttr) 498 setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, name, value)); 499 else if (name == ondurationchangeAttr) 500 setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, name, value)); 501 else if (name == onemptiedAttr) 502 setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, name, value)); 503 else if (name == onendedAttr) 504 setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, name, value)); 505 else if (name == onerrorAttr) 506 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, name, value)); 507 else if (name == onloadeddataAttr) 508 setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, name, value)); 509 else if (name == onloadedmetadataAttr) 510 setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, name, value)); 511 else if (name == onloadstartAttr) 512 setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, name, value)); 513 else if (name == onpauseAttr) 514 setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, name, value)); 515 else if (name == onplayAttr) 516 setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, name, value)); 517 else if (name == onplayingAttr) 518 setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, name, value)); 519 else if (name == onprogressAttr) 520 setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, name, value)); 521 else if (name == onratechangeAttr) 522 setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, name, value)); 523 else if (name == onseekedAttr) 524 setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, name, value)); 525 else if (name == onseekingAttr) 526 setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, name, value)); 527 else if (name == onstalledAttr) 528 setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, name, value)); 529 else if (name == onsuspendAttr) 530 setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, name, value)); 531 else if (name == ontimeupdateAttr) 532 setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, name, value)); 533 else if (name == onvolumechangeAttr) 534 setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, name, value)); 535 else if (name == onwaitingAttr) 536 setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, name, value)); 537 else if (name == onwebkitbeginfullscreenAttr) 538 setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, name, value)); 539 else if (name == onwebkitendfullscreenAttr) 540 setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, name, value)); 541 else 542 HTMLElement::parseAttribute(name, value); 543} 544 545void HTMLMediaElement::finishParsingChildren() 546{ 547 HTMLElement::finishParsingChildren(); 548 m_parsingInProgress = false; 549 550#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 551 document()->updateStyleIfNeeded(); 552 createMediaPlayerProxy(); 553#endif 554 555#if ENABLE(VIDEO_TRACK) 556 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 557 return; 558 559 for (Node* node = firstChild(); node; node = node->nextSibling()) { 560 if (node->hasTagName(trackTag)) { 561 scheduleDelayedAction(ConfigureTextTracks); 562 break; 563 } 564 } 565#endif 566} 567 568bool HTMLMediaElement::rendererIsNeeded(const NodeRenderingContext& context) 569{ 570#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 571 UNUSED_PARAM(context); 572 Frame* frame = document()->frame(); 573 if (!frame) 574 return false; 575 576 return true; 577#else 578 return controls() ? HTMLElement::rendererIsNeeded(context) : false; 579#endif 580} 581 582RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*) 583{ 584#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 585 // Setup the renderer if we already have a proxy widget. 586 RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this); 587 if (m_proxyWidget) { 588 mediaRenderer->setWidget(m_proxyWidget); 589 590 if (Frame* frame = document()->frame()) 591 frame->loader()->client()->showMediaPlayerProxyPlugin(m_proxyWidget.get()); 592 } 593 return mediaRenderer; 594#else 595 return new (arena) RenderMedia(this); 596#endif 597} 598 599bool HTMLMediaElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const 600{ 601 if (!hasMediaControls()) 602 return false; 603 // <media> doesn't allow its content, including shadow subtree, to 604 // be rendered. So this should return false for most of the children. 605 // One exception is a shadow tree built for rendering controls which should be visible. 606 // So we let them go here by comparing its subtree root with one of the controls. 607 return (mediaControls()->treeScope() == childContext.node()->treeScope() 608 && childContext.isOnUpperEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext)); 609} 610 611Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode* insertionPoint) 612{ 613 LOG(Media, "HTMLMediaElement::insertedInto"); 614 615 HTMLElement::insertedInto(insertionPoint); 616 if (insertionPoint->inDocument()) { 617 m_inActiveDocument = true; 618 619 if (m_networkState == NETWORK_EMPTY && !getAttribute(srcAttr).isEmpty()) 620 scheduleDelayedAction(LoadMediaResource); 621 } 622 623 configureMediaControls(); 624 return InsertionDone; 625} 626 627void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint) 628{ 629 LOG(Media, "HTMLMediaElement::removedFrom"); 630 631 m_inActiveDocument = false; 632 if (insertionPoint->inDocument()) { 633 configureMediaControls(); 634 if (m_networkState > NETWORK_EMPTY) 635 pause(); 636 if (m_isFullscreen) 637 exitFullscreen(); 638 639 if (m_player) { 640 JSC::VM* vm = JSDOMWindowBase::commonVM(); 641 JSC::JSLockHolder lock(vm); 642 643 size_t extraMemoryCost = m_player->extraMemoryCost(); 644 size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost; 645 m_reportedExtraMemoryCost = extraMemoryCost; 646 647 if (extraMemoryCostDelta > 0) 648 vm->heap.reportExtraMemoryCost(extraMemoryCostDelta); 649 } 650 } 651 652 HTMLElement::removedFrom(insertionPoint); 653} 654 655void HTMLMediaElement::attach(const AttachContext& context) 656{ 657 ASSERT(!attached()); 658 659#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 660 m_needWidgetUpdate = true; 661#endif 662 663 HTMLElement::attach(context); 664 665 if (renderer()) 666 renderer()->updateFromElement(); 667#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 668 else if (m_proxyWidget) { 669 if (Frame* frame = document()->frame()) 670 frame->loader()->client()->hideMediaPlayerProxyPlugin(m_proxyWidget.get()); 671 } 672#endif 673} 674 675void HTMLMediaElement::didRecalcStyle(StyleChange) 676{ 677 if (renderer()) 678 renderer()->updateFromElement(); 679} 680 681void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType) 682{ 683 LOG(Media, "HTMLMediaElement::scheduleLoad"); 684 685 if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaResource)) { 686#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 687 createMediaPlayerProxy(); 688#endif 689 690 prepareForLoad(); 691 setFlags(m_pendingActionFlags, LoadMediaResource); 692 } 693 694#if ENABLE(VIDEO_TRACK) 695 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (actionType & ConfigureTextTracks)) 696 setFlags(m_pendingActionFlags, ConfigureTextTracks); 697#endif 698 699#if USE(PLATFORM_TEXT_TRACK_MENU) 700 if (actionType & TextTrackChangesNotification) 701 setFlags(m_pendingActionFlags, TextTrackChangesNotification); 702#endif 703 704 m_loadTimer.startOneShot(0); 705} 706 707void HTMLMediaElement::scheduleNextSourceChild() 708{ 709 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad. 710 setFlags(m_pendingActionFlags, LoadMediaResource); 711 m_loadTimer.startOneShot(0); 712} 713 714void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) 715{ 716#if LOG_MEDIA_EVENTS 717 LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data()); 718#endif 719 RefPtr<Event> event = Event::create(eventName, false, true); 720 721 // Don't set the event target, the event queue will set it in GenericEventQueue::timerFired and setting it here 722 // will trigger an ASSERT if this element has been marked for deletion. 723 724 m_asyncEventQueue->enqueueEvent(event.release()); 725} 726 727void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) 728{ 729 RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations. 730 731#if ENABLE(VIDEO_TRACK) 732 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & ConfigureTextTracks)) 733 configureTextTracks(); 734#endif 735 736 if (m_pendingActionFlags & LoadMediaResource) { 737 if (m_loadState == LoadingFromSourceElement) 738 loadNextSourceChild(); 739 else 740 loadInternal(); 741 } 742 743#if USE(PLATFORM_TEXT_TRACK_MENU) 744 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & TextTrackChangesNotification)) 745 notifyMediaPlayerOfTextTrackChanges(); 746#endif 747 748 m_pendingActionFlags = 0; 749} 750 751PassRefPtr<MediaError> HTMLMediaElement::error() const 752{ 753 return m_error; 754} 755 756void HTMLMediaElement::setSrc(const String& url) 757{ 758 setAttribute(srcAttr, url); 759} 760 761HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const 762{ 763 return m_networkState; 764} 765 766String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const KURL& url) const 767{ 768 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType), keySystem, url, this); 769 String canPlay; 770 771 // 4.8.10.3 772 switch (support) 773 { 774 case MediaPlayer::IsNotSupported: 775 canPlay = emptyString(); 776 break; 777 case MediaPlayer::MayBeSupported: 778 canPlay = ASCIILiteral("maybe"); 779 break; 780 case MediaPlayer::IsSupported: 781 canPlay = ASCIILiteral("probably"); 782 break; 783 } 784 785 LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.stringCenterEllipsizedToLength().utf8().data(), canPlay.utf8().data()); 786 787 return canPlay; 788} 789 790void HTMLMediaElement::load() 791{ 792 RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations. 793 794 LOG(Media, "HTMLMediaElement::load()"); 795 796 if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture()) 797 return; 798 799 m_loadInitiatedByUserGesture = ScriptController::processingUserGesture(); 800 if (m_loadInitiatedByUserGesture) 801 removeBehaviorsRestrictionsAfterFirstUserGesture(); 802 prepareForLoad(); 803 loadInternal(); 804 prepareToPlay(); 805} 806 807void HTMLMediaElement::prepareForLoad() 808{ 809 LOG(Media, "HTMLMediaElement::prepareForLoad"); 810 811 // Perform the cleanup required for the resource load algorithm to run. 812 stopPeriodicTimers(); 813 m_loadTimer.stop(); 814 m_sentEndEvent = false; 815 m_sentStalledEvent = false; 816 m_haveFiredLoadedData = false; 817 m_completelyLoaded = false; 818 m_havePreparedToPlay = false; 819 m_displayMode = Unknown; 820 821 // 1 - Abort any already-running instance of the resource selection algorithm for this element. 822 m_loadState = WaitingForSource; 823 m_currentSourceNode = 0; 824 825 // 2 - If there are any tasks from the media element's media element event task source in 826 // one of the task queues, then remove those tasks. 827 cancelPendingEventsAndCallbacks(); 828 829 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue 830 // a task to fire a simple event named abort at the media element. 831 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) 832 scheduleEvent(eventNames().abortEvent); 833 834#if ENABLE(MEDIA_SOURCE) 835 setSourceState(MediaSource::closedKeyword()); 836#endif 837 838#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 839 createMediaPlayer(); 840#else 841 if (m_player) 842 m_player->cancelLoad(); 843 else 844 createMediaPlayerProxy(); 845#endif 846 847 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps 848 if (m_networkState != NETWORK_EMPTY) { 849 m_networkState = NETWORK_EMPTY; 850 m_readyState = HAVE_NOTHING; 851 m_readyStateMaximum = HAVE_NOTHING; 852 refreshCachedTime(); 853 m_paused = true; 854 m_seeking = false; 855 invalidateCachedTime(); 856 scheduleEvent(eventNames().emptiedEvent); 857 updateMediaController(); 858#if ENABLE(VIDEO_TRACK) 859 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 860 updateActiveTextTrackCues(0); 861#endif 862 } 863 864 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute. 865 setPlaybackRate(defaultPlaybackRate()); 866 867 // 6 - Set the error attribute to null and the autoplaying flag to true. 868 m_error = 0; 869 m_autoplaying = true; 870 871 // 7 - Invoke the media element's resource selection algorithm. 872 873 // 8 - Note: Playback of any previously playing media resource for this element stops. 874 875 // The resource selection algorithm 876 // 1 - Set the networkState to NETWORK_NO_SOURCE 877 m_networkState = NETWORK_NO_SOURCE; 878 879 // 2 - Asynchronously await a stable state. 880 881 m_playedTimeRanges = TimeRanges::create(); 882 m_lastSeekTime = 0; 883 884 // The spec doesn't say to block the load event until we actually run the asynchronous section 885 // algorithm, but do it now because we won't start that until after the timer fires and the 886 // event may have already fired by then. 887 if (m_preload != MediaPlayer::None) 888 setShouldDelayLoadEvent(true); 889 890 configureMediaControls(); 891} 892 893void HTMLMediaElement::loadInternal() 894{ 895 // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps 896 // us catch those bugs more quickly without needing all the branches to align to actually 897 // trigger the event. 898 ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); 899 900 // If we can't start a load right away, start it later. 901 Page* page = document()->page(); 902 if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) { 903 setShouldDelayLoadEvent(false); 904 if (m_isWaitingUntilMediaCanStart) 905 return; 906 document()->addMediaCanStartListener(this); 907 m_isWaitingUntilMediaCanStart = true; 908 return; 909 } 910 911 clearFlags(m_pendingActionFlags, LoadMediaResource); 912 913 // Once the page has allowed an element to load media, it is free to load at will. This allows a 914 // playlist that starts in a foreground tab to continue automatically if the tab is subsequently 915 // put in the the background. 916 removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction); 917 918#if ENABLE(VIDEO_TRACK) 919 if (hasMediaControls()) 920 mediaControls()->changedClosedCaptionsVisibility(); 921 922 // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the 923 // disabled state when the element's resource selection algorithm last started". 924 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) { 925 m_textTracksWhenResourceSelectionBegan.clear(); 926 if (m_textTracks) { 927 for (unsigned i = 0; i < m_textTracks->length(); ++i) { 928 TextTrack* track = m_textTracks->item(i); 929 if (track->mode() != TextTrack::disabledKeyword()) 930 m_textTracksWhenResourceSelectionBegan.append(track); 931 } 932 } 933 } 934#endif 935 936 selectMediaResource(); 937} 938 939void HTMLMediaElement::selectMediaResource() 940{ 941 LOG(Media, "HTMLMediaElement::selectMediaResource"); 942 943 enum Mode { attribute, children }; 944 945 // 3 - If the media element has a src attribute, then let mode be attribute. 946 Mode mode = attribute; 947 if (!fastHasAttribute(srcAttr)) { 948 Node* node; 949 for (node = firstChild(); node; node = node->nextSibling()) { 950 if (node->hasTagName(sourceTag)) 951 break; 952 } 953 954 // Otherwise, if the media element does not have a src attribute but has a source 955 // element child, then let mode be children and let candidate be the first such 956 // source element child in tree order. 957 if (node) { 958 mode = children; 959 m_nextChildNodeToConsider = node; 960 m_currentSourceNode = 0; 961 } else { 962 // Otherwise the media element has neither a src attribute nor a source element 963 // child: set the networkState to NETWORK_EMPTY, and abort these steps; the 964 // synchronous section ends. 965 m_loadState = WaitingForSource; 966 setShouldDelayLoadEvent(false); 967 m_networkState = NETWORK_EMPTY; 968 969 LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load"); 970 return; 971 } 972 } 973 974 // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event), 975 // and set its networkState to NETWORK_LOADING. 976 setShouldDelayLoadEvent(true); 977 m_networkState = NETWORK_LOADING; 978 979 // 5 - Queue a task to fire a simple event named loadstart at the media element. 980 scheduleEvent(eventNames().loadstartEvent); 981 982 // 6 - If mode is attribute, then run these substeps 983 if (mode == attribute) { 984 m_loadState = LoadingFromSrcAttr; 985 986 // If the src attribute's value is the empty string ... jump down to the failed step below 987 KURL mediaURL = getNonEmptyURLAttribute(srcAttr); 988 if (mediaURL.isEmpty()) { 989 mediaLoadingFailed(MediaPlayer::FormatError); 990 LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'"); 991 return; 992 } 993 994 if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) { 995 mediaLoadingFailed(MediaPlayer::FormatError); 996 return; 997 } 998 999 // No type or key system information is available when the url comes 1000 // from the 'src' attribute so MediaPlayer 1001 // will have to pick a media engine based on the file extension. 1002 ContentType contentType((String())); 1003 loadResource(mediaURL, contentType, String()); 1004 LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url"); 1005 return; 1006 } 1007 1008 // Otherwise, the source elements will be used 1009 loadNextSourceChild(); 1010} 1011 1012void HTMLMediaElement::loadNextSourceChild() 1013{ 1014 ContentType contentType((String())); 1015 String keySystem; 1016 KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain); 1017 if (!mediaURL.isValid()) { 1018 waitForSourceChange(); 1019 return; 1020 } 1021 1022#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1023 // Recreate the media player for the new url 1024 createMediaPlayer(); 1025#endif 1026 1027 m_loadState = LoadingFromSourceElement; 1028 loadResource(mediaURL, contentType, keySystem); 1029} 1030 1031static KURL createFileURLForApplicationCacheResource(const String& path) 1032{ 1033 // KURL should have a function to create a url from a path, but it does not. This function 1034 // is not suitable because KURL::setPath uses encodeWithURLEscapeSequences, which it notes 1035 // does not correctly escape '#' and '?'. This function works for our purposes because 1036 // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()). 1037 1038#if USE(CF) && PLATFORM(WIN) 1039 RetainPtr<CFURLRef> cfURL = adoptCF(CFURLCreateWithFileSystemPath(0, path.createCFString().get(), kCFURLWindowsPathStyle, false)); 1040 KURL url(cfURL.get()); 1041#else 1042 KURL url; 1043 1044 url.setProtocol(ASCIILiteral("file")); 1045 url.setPath(path); 1046#endif 1047 return url; 1048} 1049 1050void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType, const String& keySystem) 1051{ 1052 ASSERT(isSafeToLoadURL(initialURL, Complain)); 1053 1054 LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLoggingMedia(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data()); 1055 1056 Frame* frame = document()->frame(); 1057 if (!frame) { 1058 mediaLoadingFailed(MediaPlayer::FormatError); 1059 return; 1060 } 1061 1062 KURL url = initialURL; 1063 if (!frame->loader()->willLoadMediaElementURL(url)) { 1064 mediaLoadingFailed(MediaPlayer::FormatError); 1065 return; 1066 } 1067 1068 // The resource fetch algorithm 1069 m_networkState = NETWORK_LOADING; 1070 1071 // If the url should be loaded from the application cache, pass the url of the cached file 1072 // to the media engine. 1073 ApplicationCacheHost* cacheHost = frame->loader()->documentLoader()->applicationCacheHost(); 1074 ApplicationCacheResource* resource = 0; 1075 if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) { 1076 // Resources that are not present in the manifest will always fail to load (at least, after the 1077 // cache has been primed the first time), making the testing of offline applications simpler. 1078 if (!resource || resource->path().isEmpty()) { 1079 mediaLoadingFailed(MediaPlayer::NetworkError); 1080 return; 1081 } 1082 } 1083 1084 // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app 1085 // cache is an internal detail not exposed through the media element API. 1086 m_currentSrc = url; 1087 1088 if (resource) { 1089 url = createFileURLForApplicationCacheResource(resource->path()); 1090 LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLoggingMedia(url).utf8().data()); 1091 } 1092 1093 LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLoggingMedia(m_currentSrc).utf8().data()); 1094 1095#if ENABLE(MEDIA_STREAM) 1096 if (MediaStreamRegistry::registry().lookupMediaStreamDescriptor(url.string())) 1097 removeBehaviorRestriction(RequireUserGestureForRateChangeRestriction); 1098#endif 1099 1100 if (m_sendProgressEvents) 1101 startProgressEventTimer(); 1102 1103 Settings* settings = document()->settings(); 1104 bool privateMode = !settings || settings->privateBrowsingEnabled(); 1105 m_player->setPrivateBrowsingMode(privateMode); 1106 1107 // Reset display mode to force a recalculation of what to show because we are resetting the player. 1108 setDisplayMode(Unknown); 1109 1110 if (!autoplay()) 1111 m_player->setPreload(m_preload); 1112 m_player->setPreservesPitch(m_webkitPreservesPitch); 1113 1114 if (fastHasAttribute(mutedAttr)) 1115 m_muted = true; 1116 updateVolume(); 1117 1118#if ENABLE(MEDIA_SOURCE) 1119 ASSERT(!m_mediaSource); 1120 1121 if (url.protocolIs(mediaSourceBlobProtocol)) 1122 m_mediaSource = MediaSourceRegistry::registry().lookupMediaSource(url.string()); 1123 1124 if (m_mediaSource) { 1125 if (!m_player->load(url, m_mediaSource)) 1126 mediaLoadingFailed(MediaPlayer::FormatError); 1127 } else 1128#endif 1129 if (!m_player->load(url, contentType, keySystem)) 1130 mediaLoadingFailed(MediaPlayer::FormatError); 1131 1132 // If there is no poster to display, allow the media engine to render video frames as soon as 1133 // they are available. 1134 updateDisplayState(); 1135 1136 if (renderer()) 1137 renderer()->updateFromElement(); 1138} 1139 1140#if ENABLE(VIDEO_TRACK) 1141static bool trackIndexCompare(TextTrack* a, 1142 TextTrack* b) 1143{ 1144 return a->trackIndex() - b->trackIndex() < 0; 1145} 1146 1147static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a, 1148 const std::pair<double, TextTrackCue*>& b) 1149{ 1150 // 12 - Sort the tasks in events in ascending time order (tasks with earlier 1151 // times first). 1152 if (a.first != b.first) 1153 return a.first - b.first < 0; 1154 1155 // If the cues belong to different text tracks, it doesn't make sense to 1156 // compare the two tracks by the relative cue order, so return the relative 1157 // track order. 1158 if (a.second->track() != b.second->track()) 1159 return trackIndexCompare(a.second->track(), b.second->track()); 1160 1161 // 12 - Further sort tasks in events that have the same time by the 1162 // relative text track cue order of the text track cues associated 1163 // with these tasks. 1164 return a.second->cueIndex() - b.second->cueIndex() < 0; 1165} 1166 1167static bool compareCueInterval(const CueInterval& one, const CueInterval& two) 1168{ 1169 return one.data()->isOrderedBefore(two.data()); 1170}; 1171 1172 1173void HTMLMediaElement::updateActiveTextTrackCues(double movieTime) 1174{ 1175 // 4.8.10.8 Playing the media resource 1176 1177 // If the current playback position changes while the steps are running, 1178 // then the user agent must wait for the steps to complete, and then must 1179 // immediately rerun the steps. 1180 if (ignoreTrackDisplayUpdateRequests()) 1181 return; 1182 1183 LOG(Media, "HTMLMediaElement::updateActiveTextTracks"); 1184 1185 // 1 - Let current cues be a list of cues, initialized to contain all the 1186 // cues of all the hidden, showing, or showing by default text tracks of the 1187 // media element (not the disabled ones) whose start times are less than or 1188 // equal to the current playback position and whose end times are greater 1189 // than the current playback position. 1190 CueList currentCues; 1191 1192 // The user agent must synchronously unset [the text track cue active] flag 1193 // whenever ... the media element's readyState is changed back to HAVE_NOTHING. 1194 if (m_readyState != HAVE_NOTHING && m_player) { 1195 currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime)); 1196 std::sort(currentCues.begin(), currentCues.end(), &compareCueInterval); 1197 } 1198 1199 CueList previousCues; 1200 CueList missedCues; 1201 1202 // 2 - Let other cues be a list of cues, initialized to contain all the cues 1203 // of hidden, showing, and showing by default text tracks of the media 1204 // element that are not present in current cues. 1205 previousCues = m_currentlyActiveCues; 1206 1207 // 3 - Let last time be the current playback position at the time this 1208 // algorithm was last run for this media element, if this is not the first 1209 // time it has run. 1210 double lastTime = m_lastTextTrackUpdateTime; 1211 1212 // 4 - If the current playback position has, since the last time this 1213 // algorithm was run, only changed through its usual monotonic increase 1214 // during normal playback, then let missed cues be the list of cues in other 1215 // cues whose start times are greater than or equal to last time and whose 1216 // end times are less than or equal to the current playback position. 1217 // Otherwise, let missed cues be an empty list. 1218 if (lastTime >= 0 && m_lastSeekTime < movieTime) { 1219 CueList potentiallySkippedCues = 1220 m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime)); 1221 1222 for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) { 1223 double cueStartTime = potentiallySkippedCues[i].low(); 1224 double cueEndTime = potentiallySkippedCues[i].high(); 1225 1226 // Consider cues that may have been missed since the last seek time. 1227 if (cueStartTime > max(m_lastSeekTime, lastTime) && cueEndTime < movieTime) 1228 missedCues.append(potentiallySkippedCues[i]); 1229 } 1230 } 1231 1232 m_lastTextTrackUpdateTime = movieTime; 1233 1234 // 5 - If the time was reached through the usual monotonic increase of the 1235 // current playback position during normal playback, and if the user agent 1236 // has not fired a timeupdate event at the element in the past 15 to 250ms 1237 // and is not still running event handlers for such an event, then the user 1238 // agent must queue a task to fire a simple event named timeupdate at the 1239 // element. (In the other cases, such as explicit seeks, relevant events get 1240 // fired as part of the overall process of changing the current playback 1241 // position.) 1242 if (m_lastSeekTime <= lastTime) 1243 scheduleTimeupdateEvent(false); 1244 1245 // Explicitly cache vector sizes, as their content is constant from here. 1246 size_t currentCuesSize = currentCues.size(); 1247 size_t missedCuesSize = missedCues.size(); 1248 size_t previousCuesSize = previousCues.size(); 1249 1250 // 6 - If all of the cues in current cues have their text track cue active 1251 // flag set, none of the cues in other cues have their text track cue active 1252 // flag set, and missed cues is empty, then abort these steps. 1253 bool activeSetChanged = missedCuesSize; 1254 1255 for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i) 1256 if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive()) 1257 activeSetChanged = true; 1258 1259 for (size_t i = 0; i < currentCuesSize; ++i) { 1260 currentCues[i].data()->updateDisplayTree(movieTime); 1261 1262 if (!currentCues[i].data()->isActive()) 1263 activeSetChanged = true; 1264 } 1265 1266 if (!activeSetChanged) { 1267 // Even though the active set has not changed, it is possible that the 1268 // the mode of a track has changed from 'hidden' to 'showing' and the 1269 // cues have not yet been rendered. 1270 // Note: don't call updateTextTrackDisplay() unless we have controls because it will 1271 // create them. 1272 if (hasMediaControls()) 1273 updateTextTrackDisplay(); 1274 return; 1275 } 1276 1277 // 7 - If the time was reached through the usual monotonic increase of the 1278 // current playback position during normal playback, and there are cues in 1279 // other cues that have their text track cue pause-on-exi flag set and that 1280 // either have their text track cue active flag set or are also in missed 1281 // cues, then immediately pause the media element. 1282 for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) { 1283 if (previousCues[i].data()->pauseOnExit() 1284 && previousCues[i].data()->isActive() 1285 && !currentCues.contains(previousCues[i])) 1286 pause(); 1287 } 1288 1289 for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) { 1290 if (missedCues[i].data()->pauseOnExit()) 1291 pause(); 1292 } 1293 1294 // 8 - Let events be a list of tasks, initially empty. Each task in this 1295 // list will be associated with a text track, a text track cue, and a time, 1296 // which are used to sort the list before the tasks are queued. 1297 Vector<std::pair<double, TextTrackCue*> > eventTasks; 1298 1299 // 8 - Let affected tracks be a list of text tracks, initially empty. 1300 Vector<TextTrack*> affectedTracks; 1301 1302 for (size_t i = 0; i < missedCuesSize; ++i) { 1303 // 9 - For each text track cue in missed cues, prepare an event named enter 1304 // for the TextTrackCue object with the text track cue start time. 1305 eventTasks.append(std::make_pair(missedCues[i].data()->startTime(), 1306 missedCues[i].data())); 1307 1308 // 10 - For each text track [...] in missed cues, prepare an event 1309 // named exit for the TextTrackCue object with the with the later of 1310 // the text track cue end time and the text track cue start time. 1311 1312 // Note: An explicit task is added only if the cue is NOT a zero or 1313 // negative length cue. Otherwise, the need for an exit event is 1314 // checked when these tasks are actually queued below. This doesn't 1315 // affect sorting events before dispatch either, because the exit 1316 // event has the same time as the enter event. 1317 if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime()) 1318 eventTasks.append(std::make_pair(missedCues[i].data()->endTime(), 1319 missedCues[i].data())); 1320 } 1321 1322 for (size_t i = 0; i < previousCuesSize; ++i) { 1323 // 10 - For each text track cue in other cues that has its text 1324 // track cue active flag set prepare an event named exit for the 1325 // TextTrackCue object with the text track cue end time. 1326 if (!currentCues.contains(previousCues[i])) 1327 eventTasks.append(std::make_pair(previousCues[i].data()->endTime(), 1328 previousCues[i].data())); 1329 } 1330 1331 for (size_t i = 0; i < currentCuesSize; ++i) { 1332 // 11 - For each text track cue in current cues that does not have its 1333 // text track cue active flag set, prepare an event named enter for the 1334 // TextTrackCue object with the text track cue start time. 1335 if (!previousCues.contains(currentCues[i])) 1336 eventTasks.append(std::make_pair(currentCues[i].data()->startTime(), 1337 currentCues[i].data())); 1338 } 1339 1340 // 12 - Sort the tasks in events in ascending time order (tasks with earlier 1341 // times first). 1342 nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare); 1343 1344 for (size_t i = 0; i < eventTasks.size(); ++i) { 1345 if (!affectedTracks.contains(eventTasks[i].second->track())) 1346 affectedTracks.append(eventTasks[i].second->track()); 1347 1348 // 13 - Queue each task in events, in list order. 1349 RefPtr<Event> event; 1350 1351 // Each event in eventTasks may be either an enterEvent or an exitEvent, 1352 // depending on the time that is associated with the event. This 1353 // correctly identifies the type of the event, if the startTime is 1354 // less than the endTime in the cue. 1355 if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) { 1356 event = Event::create(eventNames().enterEvent, false, false); 1357 event->setTarget(eventTasks[i].second); 1358 m_asyncEventQueue->enqueueEvent(event.release()); 1359 1360 event = Event::create(eventNames().exitEvent, false, false); 1361 event->setTarget(eventTasks[i].second); 1362 m_asyncEventQueue->enqueueEvent(event.release()); 1363 } else { 1364 if (eventTasks[i].first == eventTasks[i].second->startTime()) 1365 event = Event::create(eventNames().enterEvent, false, false); 1366 else 1367 event = Event::create(eventNames().exitEvent, false, false); 1368 1369 event->setTarget(eventTasks[i].second); 1370 m_asyncEventQueue->enqueueEvent(event.release()); 1371 } 1372 } 1373 1374 // 14 - Sort affected tracks in the same order as the text tracks appear in 1375 // the media element's list of text tracks, and remove duplicates. 1376 nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare); 1377 1378 // 15 - For each text track in affected tracks, in the list order, queue a 1379 // task to fire a simple event named cuechange at the TextTrack object, and, ... 1380 for (size_t i = 0; i < affectedTracks.size(); ++i) { 1381 RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false); 1382 event->setTarget(affectedTracks[i]); 1383 1384 m_asyncEventQueue->enqueueEvent(event.release()); 1385 1386 // ... if the text track has a corresponding track element, to then fire a 1387 // simple event named cuechange at the track element as well. 1388 if (affectedTracks[i]->trackType() == TextTrack::TrackElement) { 1389 RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false); 1390 HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement(); 1391 ASSERT(trackElement); 1392 event->setTarget(trackElement); 1393 1394 m_asyncEventQueue->enqueueEvent(event.release()); 1395 } 1396 } 1397 1398 // 16 - Set the text track cue active flag of all the cues in the current 1399 // cues, and unset the text track cue active flag of all the cues in the 1400 // other cues. 1401 for (size_t i = 0; i < currentCuesSize; ++i) 1402 currentCues[i].data()->setIsActive(true); 1403 1404 for (size_t i = 0; i < previousCuesSize; ++i) 1405 if (!currentCues.contains(previousCues[i])) 1406 previousCues[i].data()->setIsActive(false); 1407 1408 // Update the current active cues. 1409 m_currentlyActiveCues = currentCues; 1410 1411 if (activeSetChanged) 1412 updateTextTrackDisplay(); 1413} 1414 1415bool HTMLMediaElement::textTracksAreReady() const 1416{ 1417 // 4.8.10.12.1 Text track model 1418 // ... 1419 // The text tracks of a media element are ready if all the text tracks whose mode was not 1420 // in the disabled state when the element's resource selection algorithm last started now 1421 // have a text track readiness state of loaded or failed to load. 1422 for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) { 1423 if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading 1424 || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded) 1425 return false; 1426 } 1427 1428 return true; 1429} 1430 1431void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track) 1432{ 1433 if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) { 1434 if (track->readinessState() != TextTrack::Loading) 1435 setReadyState(m_player->readyState()); 1436 } else { 1437 // The track readiness state might have changed as a result of the user 1438 // clicking the captions button. In this case, a check whether all the 1439 // resources have failed loading should be done in order to hide the CC button. 1440 if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad) 1441 mediaControls()->refreshClosedCaptionsButtonVisibility(); 1442 } 1443} 1444 1445void HTMLMediaElement::audioTrackEnabledChanged(AudioTrack*) 1446{ 1447 // We will want to change the media controls here once they exist 1448} 1449 1450void HTMLMediaElement::textTrackModeChanged(TextTrack* track) 1451{ 1452 bool trackIsLoaded = true; 1453 if (track->trackType() == TextTrack::TrackElement) { 1454 trackIsLoaded = false; 1455 for (Node* node = firstChild(); node; node = node->nextSibling()) { 1456 if (!node->hasTagName(trackTag)) 1457 continue; 1458 1459 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node); 1460 if (trackElement->track() == track) { 1461 if (trackElement->readyState() == HTMLTrackElement::LOADING || trackElement->readyState() == HTMLTrackElement::LOADED) 1462 trackIsLoaded = true; 1463 break; 1464 } 1465 } 1466 } 1467 1468 // If this is the first added track, create the list of text tracks. 1469 if (!m_textTracks) 1470 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext()); 1471 1472 // Mark this track as "configured" so configureTextTracks won't change the mode again. 1473 track->setHasBeenConfigured(true); 1474 1475 if (track->mode() != TextTrack::disabledKeyword() && trackIsLoaded) 1476 textTrackAddCues(track, track->cues()); 1477 1478#if USE(PLATFORM_TEXT_TRACK_MENU) 1479 if (platformTextTrackMenu()) 1480 platformTextTrackMenu()->trackWasSelected(track->platformTextTrack()); 1481#endif 1482 1483 configureTextTrackDisplay(); 1484 updateActiveTextTrackCues(currentTime()); 1485} 1486 1487void HTMLMediaElement::videoTrackSelectedChanged(VideoTrack*) 1488{ 1489 // We will want to change the media controls here once they exist 1490} 1491 1492void HTMLMediaElement::textTrackKindChanged(TextTrack* track) 1493{ 1494 if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword()) 1495 track->setMode(TextTrack::hiddenKeyword()); 1496} 1497 1498void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests() 1499{ 1500 ++m_ignoreTrackDisplayUpdate; 1501} 1502 1503void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests() 1504{ 1505 ASSERT(m_ignoreTrackDisplayUpdate); 1506 --m_ignoreTrackDisplayUpdate; 1507 if (!m_ignoreTrackDisplayUpdate && m_inActiveDocument) 1508 updateActiveTextTrackCues(currentTime()); 1509} 1510 1511void HTMLMediaElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues) 1512{ 1513 if (track->mode() == TextTrack::disabledKeyword()) 1514 return; 1515 1516 TrackDisplayUpdateScope scope(this); 1517 for (size_t i = 0; i < cues->length(); ++i) 1518 textTrackAddCue(track, cues->item(i)); 1519} 1520 1521void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues) 1522{ 1523 TrackDisplayUpdateScope scope(this); 1524 for (size_t i = 0; i < cues->length(); ++i) 1525 textTrackRemoveCue(cues->item(i)->track(), cues->item(i)); 1526} 1527 1528void HTMLMediaElement::textTrackAddCue(TextTrack* track, PassRefPtr<TextTrackCue> cue) 1529{ 1530 if (track->mode() == TextTrack::disabledKeyword()) 1531 return; 1532 1533 // Negative duration cues need be treated in the interval tree as 1534 // zero-length cues. 1535 double endTime = max(cue->startTime(), cue->endTime()); 1536 1537 CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get()); 1538 if (!m_cueTree.contains(interval)) 1539 m_cueTree.add(interval); 1540 updateActiveTextTrackCues(currentTime()); 1541} 1542 1543void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue) 1544{ 1545 // Negative duration cues need to be treated in the interval tree as 1546 // zero-length cues. 1547 double endTime = max(cue->startTime(), cue->endTime()); 1548 1549 CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get()); 1550 m_cueTree.remove(interval); 1551 1552 size_t index = m_currentlyActiveCues.find(interval); 1553 if (index != notFound) { 1554 cue->setIsActive(false); 1555 m_currentlyActiveCues.remove(index); 1556 } 1557 1558 cue->removeDisplayTree(); 1559 updateActiveTextTrackCues(currentTime()); 1560} 1561 1562#endif 1563 1564bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid) 1565{ 1566 if (!url.isValid()) { 1567 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLoggingMedia(url).utf8().data()); 1568 return false; 1569 } 1570 1571 Frame* frame = document()->frame(); 1572 if (!frame || !document()->securityOrigin()->canDisplay(url)) { 1573 if (actionIfInvalid == Complain) 1574 FrameLoader::reportLocalLoadFailed(frame, url.stringCenterEllipsizedToLength()); 1575 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLoggingMedia(url).utf8().data()); 1576 return false; 1577 } 1578 1579 if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) { 1580 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLoggingMedia(url).utf8().data()); 1581 return false; 1582 } 1583 1584 return true; 1585} 1586 1587void HTMLMediaElement::startProgressEventTimer() 1588{ 1589 if (m_progressEventTimer.isActive()) 1590 return; 1591 1592 m_previousProgressTime = WTF::currentTime(); 1593 // 350ms is not magic, it is in the spec! 1594 m_progressEventTimer.startRepeating(0.350); 1595} 1596 1597void HTMLMediaElement::waitForSourceChange() 1598{ 1599 LOG(Media, "HTMLMediaElement::waitForSourceChange"); 1600 1601 stopPeriodicTimers(); 1602 m_loadState = WaitingForSource; 1603 1604 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value 1605 m_networkState = NETWORK_NO_SOURCE; 1606 1607 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 1608 setShouldDelayLoadEvent(false); 1609 1610 updateDisplayState(); 1611 1612 if (renderer()) 1613 renderer()->updateFromElement(); 1614} 1615 1616void HTMLMediaElement::noneSupported() 1617{ 1618 LOG(Media, "HTMLMediaElement::noneSupported"); 1619 1620 stopPeriodicTimers(); 1621 m_loadState = WaitingForSource; 1622 m_currentSourceNode = 0; 1623 1624 // 4.8.10.5 1625 // 6 - Reaching this step indicates that the media resource failed to load or that the given 1626 // URL could not be resolved. In one atomic operation, run the following steps: 1627 1628 // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to 1629 // MEDIA_ERR_SRC_NOT_SUPPORTED. 1630 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); 1631 1632 // 6.2 - Forget the media element's media-resource-specific text tracks. 1633 1634 // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value. 1635 m_networkState = NETWORK_NO_SOURCE; 1636 1637 // 7 - Queue a task to fire a simple event named error at the media element. 1638 scheduleEvent(eventNames().errorEvent); 1639 1640#if ENABLE(MEDIA_SOURCE) 1641 setSourceState(MediaSource::closedKeyword()); 1642#endif 1643 1644 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 1645 setShouldDelayLoadEvent(false); 1646 1647 // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed, 1648 // the element won't attempt to load another resource. 1649 1650 updateDisplayState(); 1651 1652 if (renderer()) 1653 renderer()->updateFromElement(); 1654} 1655 1656void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) 1657{ 1658 LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code())); 1659 1660 // 1 - The user agent should cancel the fetching process. 1661 stopPeriodicTimers(); 1662 m_loadState = WaitingForSource; 1663 1664 // 2 - Set the error attribute to a new MediaError object whose code attribute is 1665 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE. 1666 m_error = err; 1667 1668 // 3 - Queue a task to fire a simple event named error at the media element. 1669 scheduleEvent(eventNames().errorEvent); 1670 1671#if ENABLE(MEDIA_SOURCE) 1672 setSourceState(MediaSource::closedKeyword()); 1673#endif 1674 1675 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a 1676 // task to fire a simple event called emptied at the element. 1677 m_networkState = NETWORK_EMPTY; 1678 scheduleEvent(eventNames().emptiedEvent); 1679 1680 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 1681 setShouldDelayLoadEvent(false); 1682 1683 // 6 - Abort the overall resource selection algorithm. 1684 m_currentSourceNode = 0; 1685} 1686 1687void HTMLMediaElement::cancelPendingEventsAndCallbacks() 1688{ 1689 LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks"); 1690 m_asyncEventQueue->cancelAllEvents(); 1691 1692 for (Node* node = firstChild(); node; node = node->nextSibling()) { 1693 if (node->hasTagName(sourceTag)) 1694 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent(); 1695 } 1696} 1697 1698Document* HTMLMediaElement::mediaPlayerOwningDocument() 1699{ 1700 Document* d = document(); 1701 1702 if (!d) 1703 d = ownerDocument(); 1704 1705 return d; 1706} 1707 1708void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) 1709{ 1710 beginProcessingMediaPlayerCallback(); 1711 setNetworkState(m_player->networkState()); 1712 endProcessingMediaPlayerCallback(); 1713} 1714 1715static void logMediaLoadRequest(Page* page, const String& mediaEngine, const String& errorMessage, bool succeeded) 1716{ 1717 if (!page || !page->settings()->diagnosticLoggingEnabled()) 1718 return; 1719 1720 ChromeClient* client = page->chrome().client(); 1721 1722 if (!succeeded) { 1723 client->logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadingFailedKey(), errorMessage, DiagnosticLoggingKeys::failKey()); 1724 return; 1725 } 1726 1727 client->logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), mediaEngine, DiagnosticLoggingKeys::noopKey()); 1728 1729 if (!page->hasSeenAnyMediaEngine()) 1730 client->logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey(), emptyString(), DiagnosticLoggingKeys::noopKey()); 1731 1732 if (!page->hasSeenMediaEngine(mediaEngine)) 1733 client->logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsMediaEngineKey(), mediaEngine, DiagnosticLoggingKeys::noopKey()); 1734 1735 page->sawMediaEngine(mediaEngine); 1736} 1737 1738static String stringForNetworkState(MediaPlayer::NetworkState state) 1739{ 1740 switch (state) { 1741 case MediaPlayer::Empty: return ASCIILiteral("Empty"); 1742 case MediaPlayer::Idle: return ASCIILiteral("Idle"); 1743 case MediaPlayer::Loading: return ASCIILiteral("Loading"); 1744 case MediaPlayer::Loaded: return ASCIILiteral("Loaded"); 1745 case MediaPlayer::FormatError: return ASCIILiteral("FormatError"); 1746 case MediaPlayer::NetworkError: return ASCIILiteral("NetworkError"); 1747 case MediaPlayer::DecodeError: return ASCIILiteral("DecodeError"); 1748 default: return emptyString(); 1749 } 1750} 1751 1752void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error) 1753{ 1754 stopPeriodicTimers(); 1755 1756 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more 1757 // <source> children, schedule the next one 1758 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) { 1759 1760 if (m_currentSourceNode) 1761 m_currentSourceNode->scheduleErrorEvent(); 1762 else 1763 LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed"); 1764 1765 if (havePotentialSourceChild()) { 1766 LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>"); 1767 scheduleNextSourceChild(); 1768 } else { 1769 LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting"); 1770 waitForSourceChange(); 1771 } 1772 1773 return; 1774 } 1775 1776 if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) 1777 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK)); 1778 else if (error == MediaPlayer::DecodeError) 1779 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE)); 1780 else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr) 1781 noneSupported(); 1782 1783 updateDisplayState(); 1784 if (hasMediaControls()) { 1785 mediaControls()->reset(); 1786 mediaControls()->reportedError(); 1787 } 1788 1789 logMediaLoadRequest(document()->page(), String(), stringForNetworkState(error), false); 1790} 1791 1792void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) 1793{ 1794 LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState)); 1795 1796 if (state == MediaPlayer::Empty) { 1797 // Just update the cached state and leave, we can't do anything. 1798 m_networkState = NETWORK_EMPTY; 1799 return; 1800 } 1801 1802 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) { 1803 mediaLoadingFailed(state); 1804 return; 1805 } 1806 1807 if (state == MediaPlayer::Idle) { 1808 if (m_networkState > NETWORK_IDLE) { 1809 changeNetworkStateFromLoadingToIdle(); 1810 setShouldDelayLoadEvent(false); 1811 } else { 1812 m_networkState = NETWORK_IDLE; 1813 } 1814 } 1815 1816 if (state == MediaPlayer::Loading) { 1817 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE) 1818 startProgressEventTimer(); 1819 m_networkState = NETWORK_LOADING; 1820 } 1821 1822 if (state == MediaPlayer::Loaded) { 1823 if (m_networkState != NETWORK_IDLE) 1824 changeNetworkStateFromLoadingToIdle(); 1825 m_completelyLoaded = true; 1826 } 1827 1828 if (hasMediaControls()) 1829 mediaControls()->updateStatusDisplay(); 1830} 1831 1832void HTMLMediaElement::changeNetworkStateFromLoadingToIdle() 1833{ 1834 m_progressEventTimer.stop(); 1835 if (hasMediaControls() && m_player->didLoadingProgress()) 1836 mediaControls()->bufferingProgressed(); 1837 1838 // Schedule one last progress event so we guarantee that at least one is fired 1839 // for files that load very quickly. 1840 scheduleEvent(eventNames().progressEvent); 1841 scheduleEvent(eventNames().suspendEvent); 1842 m_networkState = NETWORK_IDLE; 1843} 1844 1845void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) 1846{ 1847 beginProcessingMediaPlayerCallback(); 1848 1849 setReadyState(m_player->readyState()); 1850 1851 endProcessingMediaPlayerCallback(); 1852} 1853 1854void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) 1855{ 1856 LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState)); 1857 1858 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it 1859 bool wasPotentiallyPlaying = potentiallyPlaying(); 1860 1861 ReadyState oldState = m_readyState; 1862 ReadyState newState = static_cast<ReadyState>(state); 1863 1864#if ENABLE(VIDEO_TRACK) 1865 bool tracksAreReady = !RuntimeEnabledFeatures::webkitVideoTrackEnabled() || textTracksAreReady(); 1866 1867 if (newState == oldState && m_tracksAreReady == tracksAreReady) 1868 return; 1869 1870 m_tracksAreReady = tracksAreReady; 1871#else 1872 if (newState == oldState) 1873 return; 1874 bool tracksAreReady = true; 1875#endif 1876 1877 if (tracksAreReady) 1878 m_readyState = newState; 1879 else { 1880 // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until 1881 // the text tracks are ready, regardless of the state of the media file. 1882 if (newState <= HAVE_METADATA) 1883 m_readyState = newState; 1884 else 1885 m_readyState = HAVE_CURRENT_DATA; 1886 } 1887 1888 if (oldState > m_readyStateMaximum) 1889 m_readyStateMaximum = oldState; 1890 1891 if (m_networkState == NETWORK_EMPTY) 1892 return; 1893 1894 if (m_seeking) { 1895 // 4.8.10.9, step 11 1896 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) 1897 scheduleEvent(eventNames().waitingEvent); 1898 1899 // 4.8.10.10 step 14 & 15. 1900 if (m_readyState >= HAVE_CURRENT_DATA) 1901 finishSeek(); 1902 } else { 1903 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) { 1904 // 4.8.10.8 1905 scheduleTimeupdateEvent(false); 1906 scheduleEvent(eventNames().waitingEvent); 1907 } 1908 } 1909 1910 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) { 1911 prepareMediaFragmentURI(); 1912 scheduleEvent(eventNames().durationchangeEvent); 1913 scheduleEvent(eventNames().loadedmetadataEvent); 1914 if (hasMediaControls()) 1915 mediaControls()->loadedMetadata(); 1916 if (renderer()) 1917 renderer()->updateFromElement(); 1918 1919 logMediaLoadRequest(document()->page(), m_player->engineDescription(), String(), true); 1920 } 1921 1922 bool shouldUpdateDisplayState = false; 1923 1924 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) { 1925 m_haveFiredLoadedData = true; 1926 shouldUpdateDisplayState = true; 1927 scheduleEvent(eventNames().loadeddataEvent); 1928 setShouldDelayLoadEvent(false); 1929 applyMediaFragmentURI(); 1930 } 1931 1932 bool isPotentiallyPlaying = potentiallyPlaying(); 1933 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) { 1934 scheduleEvent(eventNames().canplayEvent); 1935 if (isPotentiallyPlaying) 1936 scheduleEvent(eventNames().playingEvent); 1937 shouldUpdateDisplayState = true; 1938 } 1939 1940 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) { 1941 if (oldState <= HAVE_CURRENT_DATA) 1942 scheduleEvent(eventNames().canplayEvent); 1943 1944 scheduleEvent(eventNames().canplaythroughEvent); 1945 1946 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) 1947 scheduleEvent(eventNames().playingEvent); 1948 1949 if (m_autoplaying && m_paused && autoplay() && !document()->isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForRateChange()) { 1950 m_paused = false; 1951 invalidateCachedTime(); 1952 scheduleEvent(eventNames().playEvent); 1953 scheduleEvent(eventNames().playingEvent); 1954 } 1955 1956 shouldUpdateDisplayState = true; 1957 } 1958 1959 if (shouldUpdateDisplayState) { 1960 updateDisplayState(); 1961 if (hasMediaControls()) { 1962 mediaControls()->refreshClosedCaptionsButtonVisibility(); 1963 mediaControls()->updateStatusDisplay(); 1964 } 1965 } 1966 1967 updatePlayState(); 1968 updateMediaController(); 1969#if ENABLE(VIDEO_TRACK) 1970 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 1971 updateActiveTextTrackCues(currentTime()); 1972#endif 1973} 1974 1975#if ENABLE(ENCRYPTED_MEDIA) 1976void HTMLMediaElement::mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId) 1977{ 1978 MediaKeyEventInit initializer; 1979 initializer.keySystem = keySystem; 1980 initializer.sessionId = sessionId; 1981 initializer.bubbles = false; 1982 initializer.cancelable = false; 1983 1984 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyaddedEvent, initializer); 1985 event->setTarget(this); 1986 m_asyncEventQueue->enqueueEvent(event.release()); 1987} 1988 1989void HTMLMediaElement::mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode) 1990{ 1991 MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN; 1992 switch (errorCode) { 1993 case MediaPlayerClient::UnknownError: 1994 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN; 1995 break; 1996 case MediaPlayerClient::ClientError: 1997 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT; 1998 break; 1999 case MediaPlayerClient::ServiceError: 2000 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE; 2001 break; 2002 case MediaPlayerClient::OutputError: 2003 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT; 2004 break; 2005 case MediaPlayerClient::HardwareChangeError: 2006 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE; 2007 break; 2008 case MediaPlayerClient::DomainError: 2009 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN; 2010 break; 2011 } 2012 2013 MediaKeyEventInit initializer; 2014 initializer.keySystem = keySystem; 2015 initializer.sessionId = sessionId; 2016 initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode); 2017 initializer.systemCode = systemCode; 2018 initializer.bubbles = false; 2019 initializer.cancelable = false; 2020 2021 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyerrorEvent, initializer); 2022 event->setTarget(this); 2023 m_asyncEventQueue->enqueueEvent(event.release()); 2024} 2025 2026void HTMLMediaElement::mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const KURL& defaultURL) 2027{ 2028 MediaKeyEventInit initializer; 2029 initializer.keySystem = keySystem; 2030 initializer.sessionId = sessionId; 2031 initializer.message = Uint8Array::create(message, messageLength); 2032 initializer.defaultURL = defaultURL; 2033 initializer.bubbles = false; 2034 initializer.cancelable = false; 2035 2036 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeymessageEvent, initializer); 2037 event->setTarget(this); 2038 m_asyncEventQueue->enqueueEvent(event.release()); 2039} 2040 2041bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength) 2042{ 2043 if (!hasEventListeners(eventNames().webkitneedkeyEvent)) { 2044 m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED); 2045 scheduleEvent(eventNames().errorEvent); 2046 return false; 2047 } 2048 2049 MediaKeyEventInit initializer; 2050 initializer.keySystem = keySystem; 2051 initializer.sessionId = sessionId; 2052 initializer.initData = Uint8Array::create(initData, initDataLength); 2053 initializer.bubbles = false; 2054 initializer.cancelable = false; 2055 2056 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitneedkeyEvent, initializer); 2057 event->setTarget(this); 2058 m_asyncEventQueue->enqueueEvent(event.release()); 2059 return true; 2060} 2061#endif 2062 2063#if ENABLE(ENCRYPTED_MEDIA_V2) 2064bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array* initData) 2065{ 2066 if (!hasEventListeners("webkitneedkey")) { 2067 m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED); 2068 scheduleEvent(eventNames().errorEvent); 2069 return false; 2070 } 2071 2072 MediaKeyNeededEventInit initializer; 2073 initializer.initData = initData; 2074 initializer.bubbles = false; 2075 initializer.cancelable = false; 2076 2077 RefPtr<Event> event = MediaKeyNeededEvent::create(eventNames().webkitneedkeyEvent, initializer); 2078 event->setTarget(this); 2079 m_asyncEventQueue->enqueueEvent(event.release()); 2080 2081 return true; 2082} 2083 2084void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys) 2085{ 2086 if (m_mediaKeys == mediaKeys) 2087 return; 2088 2089 if (m_mediaKeys) 2090 m_mediaKeys->setMediaElement(0); 2091 m_mediaKeys = mediaKeys; 2092 if (m_mediaKeys) 2093 m_mediaKeys->setMediaElement(this); 2094} 2095#endif 2096 2097void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) 2098{ 2099 ASSERT(m_player); 2100 if (m_networkState != NETWORK_LOADING) 2101 return; 2102 2103 double time = WTF::currentTime(); 2104 double timedelta = time - m_previousProgressTime; 2105 2106 if (m_player->didLoadingProgress()) { 2107 scheduleEvent(eventNames().progressEvent); 2108 m_previousProgressTime = time; 2109 m_sentStalledEvent = false; 2110 if (renderer()) 2111 renderer()->updateFromElement(); 2112 if (hasMediaControls()) 2113 mediaControls()->bufferingProgressed(); 2114 } else if (timedelta > 3.0 && !m_sentStalledEvent) { 2115 scheduleEvent(eventNames().stalledEvent); 2116 m_sentStalledEvent = true; 2117 setShouldDelayLoadEvent(false); 2118 } 2119} 2120 2121void HTMLMediaElement::rewind(double timeDelta) 2122{ 2123 LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta); 2124 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), IGNORE_EXCEPTION); 2125} 2126 2127void HTMLMediaElement::returnToRealtime() 2128{ 2129 LOG(Media, "HTMLMediaElement::returnToRealtime"); 2130 setCurrentTime(maxTimeSeekable(), IGNORE_EXCEPTION); 2131} 2132 2133void HTMLMediaElement::addPlayedRange(double start, double end) 2134{ 2135 LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end); 2136 if (!m_playedTimeRanges) 2137 m_playedTimeRanges = TimeRanges::create(); 2138 m_playedTimeRanges->add(start, end); 2139} 2140 2141bool HTMLMediaElement::supportsSave() const 2142{ 2143 return m_player ? m_player->supportsSave() : false; 2144} 2145 2146bool HTMLMediaElement::supportsScanning() const 2147{ 2148 return m_player ? m_player->supportsScanning() : false; 2149} 2150 2151void HTMLMediaElement::prepareToPlay() 2152{ 2153 LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this); 2154 if (m_havePreparedToPlay) 2155 return; 2156 m_havePreparedToPlay = true; 2157 m_player->prepareToPlay(); 2158} 2159 2160void HTMLMediaElement::seek(double time, ExceptionCode& ec) 2161{ 2162 LOG(Media, "HTMLMediaElement::seek(%f)", time); 2163 2164 // 4.8.9.9 Seeking 2165 2166 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception. 2167 if (m_readyState == HAVE_NOTHING || !m_player) { 2168 ec = INVALID_STATE_ERR; 2169 return; 2170 } 2171 2172 // If the media engine has been told to postpone loading data, let it go ahead now. 2173 if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA) 2174 prepareToPlay(); 2175 2176 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set. 2177 refreshCachedTime(); 2178 double now = currentTime(); 2179 2180 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is 2181 // already running. Abort that other instance of the algorithm without waiting for the step that 2182 // it is running to complete. 2183 // Nothing specific to be done here. 2184 2185 // 3 - Set the seeking IDL attribute to true. 2186 // The flag will be cleared when the engine tells us the time has actually changed. 2187 m_seeking = true; 2188 2189 // 5 - If the new playback position is later than the end of the media resource, then let it be the end 2190 // of the media resource instead. 2191 time = min(time, duration()); 2192 2193 // 6 - If the new playback position is less than the earliest possible position, let it be that position instead. 2194 double earliestTime = m_player->startTime(); 2195 time = max(time, earliestTime); 2196 2197 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This 2198 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's 2199 // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and 2200 // not generate a timechanged callback. This means m_seeking will never be cleared and we will never 2201 // fire a 'seeked' event. 2202#if !LOG_DISABLED 2203 double mediaTime = m_player->mediaTimeForTimeValue(time); 2204 if (time != mediaTime) 2205 LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime); 2206#endif 2207 time = m_player->mediaTimeForTimeValue(time); 2208 2209 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the 2210 // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute 2211 // that is the nearest to the new playback position. ... If there are no ranges given in the seekable 2212 // attribute then set the seeking IDL attribute to false and abort these steps. 2213 RefPtr<TimeRanges> seekableRanges = seekable(); 2214 2215 // Short circuit seeking to the current time by just firing the events if no seek is required. 2216 // Don't skip calling the media engine if we are in poster mode because a seek should always 2217 // cancel poster display. 2218 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster); 2219 2220#if ENABLE(MEDIA_SOURCE) 2221 // Always notify the media engine of a seek if the source is not closed. This ensures that the source is 2222 // always in a flushed state when the 'seeking' event fires. 2223 if (m_mediaSource && m_mediaSource->readyState() != MediaSource::closedKeyword()) 2224 noSeekRequired = false; 2225#endif 2226 2227 if (noSeekRequired) { 2228 if (time == now) { 2229 scheduleEvent(eventNames().seekingEvent); 2230 scheduleTimeupdateEvent(false); 2231 scheduleEvent(eventNames().seekedEvent); 2232 } 2233 m_seeking = false; 2234 return; 2235 } 2236 time = seekableRanges->nearest(time); 2237 2238 if (m_playing) { 2239 if (m_lastSeekTime < now) 2240 addPlayedRange(m_lastSeekTime, now); 2241 } 2242 m_lastSeekTime = time; 2243 m_sentEndEvent = false; 2244 2245 // 8 - Set the current playback position to the given new playback position 2246 m_player->seek(time); 2247 2248 // 9 - Queue a task to fire a simple event named seeking at the element. 2249 scheduleEvent(eventNames().seekingEvent); 2250 2251 // 10 - Queue a task to fire a simple event named timeupdate at the element. 2252 scheduleTimeupdateEvent(false); 2253 2254 // 11-15 are handled, if necessary, when the engine signals a readystate change. 2255} 2256 2257void HTMLMediaElement::finishSeek() 2258{ 2259 LOG(Media, "HTMLMediaElement::finishSeek"); 2260 2261 // 4.8.10.9 Seeking step 14 2262 m_seeking = false; 2263 2264 // 4.8.10.9 Seeking step 15 2265 scheduleEvent(eventNames().seekedEvent); 2266 2267 setDisplayMode(Video); 2268} 2269 2270HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const 2271{ 2272 return m_readyState; 2273} 2274 2275MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const 2276{ 2277 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown; 2278} 2279 2280bool HTMLMediaElement::hasAudio() const 2281{ 2282 return m_player ? m_player->hasAudio() : false; 2283} 2284 2285bool HTMLMediaElement::seeking() const 2286{ 2287 return m_seeking; 2288} 2289 2290void HTMLMediaElement::refreshCachedTime() const 2291{ 2292 m_cachedTime = m_player->currentTime(); 2293 m_cachedTimeWallClockUpdateTime = WTF::currentTime(); 2294} 2295 2296void HTMLMediaElement::invalidateCachedTime() 2297{ 2298 LOG(Media, "HTMLMediaElement::invalidateCachedTime"); 2299 2300 // Don't try to cache movie time when playback first starts as the time reported by the engine 2301 // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it 2302 // too early. 2303 static const double minimumTimePlayingBeforeCacheSnapshot = 0.5; 2304 2305 m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot; 2306 m_cachedTime = MediaPlayer::invalidTime(); 2307} 2308 2309// playback state 2310double HTMLMediaElement::currentTime() const 2311{ 2312#if LOG_CACHED_TIME_WARNINGS 2313 static const double minCachedDeltaForWarning = 0.01; 2314#endif 2315 2316 if (!m_player) 2317 return 0; 2318 2319 if (m_seeking) { 2320 LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime); 2321 return m_lastSeekTime; 2322 } 2323 2324 if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) { 2325#if LOG_CACHED_TIME_WARNINGS 2326 double delta = m_cachedTime - m_player->currentTime(); 2327 if (delta > minCachedDeltaForWarning) 2328 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta); 2329#endif 2330 return m_cachedTime; 2331 } 2332 2333 // Is it too soon use a cached time? 2334 double now = WTF::currentTime(); 2335 double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime(); 2336 2337 if (maximumDurationToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime() && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) { 2338 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; 2339 2340 // Not too soon, use the cached time only if it hasn't expired. 2341 if (wallClockDelta < maximumDurationToCacheMediaTime) { 2342 double adjustedCacheTime = m_cachedTime + (m_playbackRate * wallClockDelta); 2343 2344#if LOG_CACHED_TIME_WARNINGS 2345 double delta = adjustedCacheTime - m_player->currentTime(); 2346 if (delta > minCachedDeltaForWarning) 2347 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta); 2348#endif 2349 return adjustedCacheTime; 2350 } 2351 } 2352 2353#if LOG_CACHED_TIME_WARNINGS 2354 if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime()) { 2355 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; 2356 double delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime(); 2357 LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta); 2358 } 2359#endif 2360 2361 refreshCachedTime(); 2362 2363 return m_cachedTime; 2364} 2365 2366void HTMLMediaElement::setCurrentTime(double time, ExceptionCode& ec) 2367{ 2368 if (m_mediaController) { 2369 ec = INVALID_STATE_ERR; 2370 return; 2371 } 2372 seek(time, ec); 2373} 2374 2375double HTMLMediaElement::startTime() const 2376{ 2377 if (!m_player) 2378 return 0; 2379 return m_player->startTime(); 2380} 2381 2382double HTMLMediaElement::initialTime() const 2383{ 2384 if (m_fragmentStartTime != MediaPlayer::invalidTime()) 2385 return m_fragmentStartTime; 2386 2387 if (!m_player) 2388 return 0; 2389 2390 return m_player->initialTime(); 2391} 2392 2393double HTMLMediaElement::duration() const 2394{ 2395 if (m_player && m_readyState >= HAVE_METADATA) 2396 return m_player->duration(); 2397 2398 return numeric_limits<double>::quiet_NaN(); 2399} 2400 2401bool HTMLMediaElement::paused() const 2402{ 2403 return m_paused; 2404} 2405 2406double HTMLMediaElement::defaultPlaybackRate() const 2407{ 2408 return m_defaultPlaybackRate; 2409} 2410 2411void HTMLMediaElement::setDefaultPlaybackRate(double rate) 2412{ 2413 if (m_defaultPlaybackRate != rate) { 2414 m_defaultPlaybackRate = rate; 2415 scheduleEvent(eventNames().ratechangeEvent); 2416 } 2417} 2418 2419double HTMLMediaElement::playbackRate() const 2420{ 2421 return m_playbackRate; 2422} 2423 2424void HTMLMediaElement::setPlaybackRate(double rate) 2425{ 2426 LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate); 2427 2428 if (m_playbackRate != rate) { 2429 m_playbackRate = rate; 2430 invalidateCachedTime(); 2431 scheduleEvent(eventNames().ratechangeEvent); 2432 } 2433 2434 if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController) 2435 m_player->setRate(rate); 2436} 2437 2438void HTMLMediaElement::updatePlaybackRate() 2439{ 2440 double effectiveRate = m_mediaController ? m_mediaController->playbackRate() : m_playbackRate; 2441 if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate) 2442 m_player->setRate(effectiveRate); 2443} 2444 2445bool HTMLMediaElement::webkitPreservesPitch() const 2446{ 2447 return m_webkitPreservesPitch; 2448} 2449 2450void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) 2451{ 2452 LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch)); 2453 2454 m_webkitPreservesPitch = preservesPitch; 2455 2456 if (!m_player) 2457 return; 2458 2459 m_player->setPreservesPitch(preservesPitch); 2460} 2461 2462bool HTMLMediaElement::ended() const 2463{ 2464 // 4.8.10.8 Playing the media resource 2465 // The ended attribute must return true if the media element has ended 2466 // playback and the direction of playback is forwards, and false otherwise. 2467 return endedPlayback() && m_playbackRate > 0; 2468} 2469 2470bool HTMLMediaElement::autoplay() const 2471{ 2472 return fastHasAttribute(autoplayAttr); 2473} 2474 2475void HTMLMediaElement::setAutoplay(bool b) 2476{ 2477 LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b)); 2478 setBooleanAttribute(autoplayAttr, b); 2479} 2480 2481String HTMLMediaElement::preload() const 2482{ 2483 switch (m_preload) { 2484 case MediaPlayer::None: 2485 return ASCIILiteral("none"); 2486 break; 2487 case MediaPlayer::MetaData: 2488 return ASCIILiteral("metadata"); 2489 break; 2490 case MediaPlayer::Auto: 2491 return ASCIILiteral("auto"); 2492 break; 2493 } 2494 2495 ASSERT_NOT_REACHED(); 2496 return String(); 2497} 2498 2499void HTMLMediaElement::setPreload(const String& preload) 2500{ 2501 LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data()); 2502 setAttribute(preloadAttr, preload); 2503} 2504 2505void HTMLMediaElement::play() 2506{ 2507 LOG(Media, "HTMLMediaElement::play()"); 2508 2509 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture()) 2510 return; 2511 if (ScriptController::processingUserGesture()) 2512 removeBehaviorsRestrictionsAfterFirstUserGesture(); 2513 2514 Settings* settings = document()->settings(); 2515 if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) { 2516 // It should be impossible to be processing the canplay event while handling a user gesture 2517 // since it is dispatched asynchronously. 2518 ASSERT(!ScriptController::processingUserGesture()); 2519 String host = document()->baseURL().host(); 2520 if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org")) 2521 return; 2522 } 2523 2524 playInternal(); 2525} 2526 2527void HTMLMediaElement::playInternal() 2528{ 2529 LOG(Media, "HTMLMediaElement::playInternal"); 2530 2531 // 4.8.10.9. Playing the media resource 2532 if (!m_player || m_networkState == NETWORK_EMPTY) 2533 scheduleDelayedAction(LoadMediaResource); 2534 2535 if (endedPlayback()) 2536 seek(0, IGNORE_EXCEPTION); 2537 2538 if (m_mediaController) 2539 m_mediaController->bringElementUpToSpeed(this); 2540 2541 if (m_paused) { 2542 m_paused = false; 2543 invalidateCachedTime(); 2544 scheduleEvent(eventNames().playEvent); 2545 2546 if (m_readyState <= HAVE_CURRENT_DATA) 2547 scheduleEvent(eventNames().waitingEvent); 2548 else if (m_readyState >= HAVE_FUTURE_DATA) 2549 scheduleEvent(eventNames().playingEvent); 2550 } 2551 m_autoplaying = false; 2552 updatePlayState(); 2553 updateMediaController(); 2554} 2555 2556void HTMLMediaElement::pause() 2557{ 2558 LOG(Media, "HTMLMediaElement::pause()"); 2559 2560 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture()) 2561 return; 2562 2563 pauseInternal(); 2564} 2565 2566 2567void HTMLMediaElement::pauseInternal() 2568{ 2569 LOG(Media, "HTMLMediaElement::pauseInternal"); 2570 2571 // 4.8.10.9. Playing the media resource 2572 if (!m_player || m_networkState == NETWORK_EMPTY) 2573 scheduleDelayedAction(LoadMediaResource); 2574 2575 m_autoplaying = false; 2576 2577 if (!m_paused) { 2578 m_paused = true; 2579 scheduleTimeupdateEvent(false); 2580 scheduleEvent(eventNames().pauseEvent); 2581 } 2582 2583 updatePlayState(); 2584} 2585 2586#if ENABLE(MEDIA_SOURCE) 2587void HTMLMediaElement::setSourceState(const String& state) 2588{ 2589 if (!m_mediaSource) 2590 return; 2591 2592 m_mediaSource->setReadyState(state); 2593 if (state == MediaSource::closedKeyword()) 2594 m_mediaSource = 0; 2595} 2596#endif 2597 2598#if ENABLE(ENCRYPTED_MEDIA) 2599void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode& ec) 2600{ 2601#if ENABLE(ENCRYPTED_MEDIA_V2) 2602 static bool firstTime = true; 2603 if (firstTime && scriptExecutionContext()) { 2604 scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "'HTMLMediaElement.webkitGenerateKeyRequest()' is deprecated. Use 'MediaKeys.createSession()' instead."); 2605 firstTime = false; 2606 } 2607#endif 2608 2609 if (keySystem.isEmpty()) { 2610 ec = SYNTAX_ERR; 2611 return; 2612 } 2613 2614 if (!m_player) { 2615 ec = INVALID_STATE_ERR; 2616 return; 2617 } 2618 2619 const unsigned char* initDataPointer = 0; 2620 unsigned initDataLength = 0; 2621 if (initData) { 2622 initDataPointer = initData->data(); 2623 initDataLength = initData->length(); 2624 } 2625 2626 MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength); 2627 ec = exceptionCodeForMediaKeyException(result); 2628} 2629 2630void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionCode& ec) 2631{ 2632 webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), ec); 2633} 2634 2635void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode& ec) 2636{ 2637#if ENABLE(ENCRYPTED_MEDIA_V2) 2638 static bool firstTime = true; 2639 if (firstTime && scriptExecutionContext()) { 2640 scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "'HTMLMediaElement.webkitAddKey()' is deprecated. Use 'MediaKeySession.update()' instead."); 2641 firstTime = false; 2642 } 2643#endif 2644 2645 if (keySystem.isEmpty()) { 2646 ec = SYNTAX_ERR; 2647 return; 2648 } 2649 2650 if (!key) { 2651 ec = SYNTAX_ERR; 2652 return; 2653 } 2654 2655 if (!key->length()) { 2656 ec = TYPE_MISMATCH_ERR; 2657 return; 2658 } 2659 2660 if (!m_player) { 2661 ec = INVALID_STATE_ERR; 2662 return; 2663 } 2664 2665 const unsigned char* initDataPointer = 0; 2666 unsigned initDataLength = 0; 2667 if (initData) { 2668 initDataPointer = initData->data(); 2669 initDataLength = initData->length(); 2670 } 2671 2672 MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId); 2673 ec = exceptionCodeForMediaKeyException(result); 2674} 2675 2676void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionCode& ec) 2677{ 2678 webkitAddKey(keySystem, key, Uint8Array::create(0), String(), ec); 2679} 2680 2681void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode& ec) 2682{ 2683 if (keySystem.isEmpty()) { 2684 ec = SYNTAX_ERR; 2685 return; 2686 } 2687 2688 if (!m_player) { 2689 ec = INVALID_STATE_ERR; 2690 return; 2691 } 2692 2693 MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId); 2694 ec = exceptionCodeForMediaKeyException(result); 2695} 2696 2697#endif 2698 2699bool HTMLMediaElement::loop() const 2700{ 2701 return fastHasAttribute(loopAttr); 2702} 2703 2704void HTMLMediaElement::setLoop(bool b) 2705{ 2706 LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b)); 2707 setBooleanAttribute(loopAttr, b); 2708} 2709 2710bool HTMLMediaElement::controls() const 2711{ 2712 Frame* frame = document()->frame(); 2713 2714 // always show controls when scripting is disabled 2715 if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript)) 2716 return true; 2717 2718 // always show controls for video when fullscreen playback is required. 2719 if (isVideo() && document()->page() && document()->page()->chrome().requiresFullscreenForVideoPlayback()) 2720 return true; 2721 2722 // Always show controls when in full screen mode. 2723 if (isFullscreen()) 2724 return true; 2725 2726 return fastHasAttribute(controlsAttr); 2727} 2728 2729void HTMLMediaElement::setControls(bool b) 2730{ 2731 LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b)); 2732 setBooleanAttribute(controlsAttr, b); 2733} 2734 2735double HTMLMediaElement::volume() const 2736{ 2737 return m_volume; 2738} 2739 2740void HTMLMediaElement::setVolume(double vol, ExceptionCode& ec) 2741{ 2742 LOG(Media, "HTMLMediaElement::setVolume(%f)", vol); 2743 2744 if (vol < 0.0f || vol > 1.0f) { 2745 ec = INDEX_SIZE_ERR; 2746 return; 2747 } 2748 2749 if (m_volume != vol) { 2750 m_volume = vol; 2751 updateVolume(); 2752 scheduleEvent(eventNames().volumechangeEvent); 2753 } 2754} 2755 2756bool HTMLMediaElement::muted() const 2757{ 2758 return m_muted; 2759} 2760 2761void HTMLMediaElement::setMuted(bool muted) 2762{ 2763 LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted)); 2764 2765 if (m_muted != muted) { 2766 m_muted = muted; 2767 // Avoid recursion when the player reports volume changes. 2768 if (!processingMediaPlayerCallback()) { 2769 if (m_player) { 2770 m_player->setMuted(m_muted); 2771 if (hasMediaControls()) 2772 mediaControls()->changedMute(); 2773 } 2774 } 2775 scheduleEvent(eventNames().volumechangeEvent); 2776 } 2777} 2778 2779void HTMLMediaElement::togglePlayState() 2780{ 2781 LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay())); 2782 2783 // We can safely call the internal play/pause methods, which don't check restrictions, because 2784 // this method is only called from the built-in media controller 2785 if (canPlay()) { 2786 updatePlaybackRate(); 2787 playInternal(); 2788 } else 2789 pauseInternal(); 2790} 2791 2792void HTMLMediaElement::beginScrubbing() 2793{ 2794 LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused())); 2795 2796 if (!paused()) { 2797 if (ended()) { 2798 // Because a media element stays in non-paused state when it reaches end, playback resumes 2799 // when the slider is dragged from the end to another position unless we pause first. Do 2800 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. 2801 pause(); 2802 } else { 2803 // Not at the end but we still want to pause playback so the media engine doesn't try to 2804 // continue playing during scrubbing. Pause without generating an event as we will 2805 // unpause after scrubbing finishes. 2806 setPausedInternal(true); 2807 } 2808 } 2809} 2810 2811void HTMLMediaElement::endScrubbing() 2812{ 2813 LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal)); 2814 2815 if (m_pausedInternal) 2816 setPausedInternal(false); 2817} 2818 2819// The spec says to fire periodic timeupdate events (those sent while playing) every 2820// "15 to 250ms", we choose the slowest frequency 2821static const double maxTimeupdateEventFrequency = 0.25; 2822 2823void HTMLMediaElement::startPlaybackProgressTimer() 2824{ 2825 if (m_playbackProgressTimer.isActive()) 2826 return; 2827 2828 m_previousProgressTime = WTF::currentTime(); 2829 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); 2830} 2831 2832void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) 2833{ 2834 ASSERT(m_player); 2835 2836 if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) { 2837 m_fragmentEndTime = MediaPlayer::invalidTime(); 2838 if (!m_mediaController && !m_paused) { 2839 // changes paused to true and fires a simple event named pause at the media element. 2840 pauseInternal(); 2841 } 2842 } 2843 2844 scheduleTimeupdateEvent(true); 2845 2846 if (!m_playbackRate) 2847 return; 2848 2849 if (!m_paused && hasMediaControls()) 2850 mediaControls()->playbackProgressed(); 2851 2852#if ENABLE(VIDEO_TRACK) 2853 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 2854 updateActiveTextTrackCues(currentTime()); 2855#endif 2856} 2857 2858void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) 2859{ 2860 double now = WTF::currentTime(); 2861 double timedelta = now - m_lastTimeUpdateEventWallTime; 2862 2863 // throttle the periodic events 2864 if (periodicEvent && timedelta < maxTimeupdateEventFrequency) 2865 return; 2866 2867 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one 2868 // event at a given time so filter here 2869 double movieTime = currentTime(); 2870 if (movieTime != m_lastTimeUpdateEventMovieTime) { 2871 scheduleEvent(eventNames().timeupdateEvent); 2872 m_lastTimeUpdateEventWallTime = now; 2873 m_lastTimeUpdateEventMovieTime = movieTime; 2874 } 2875} 2876 2877bool HTMLMediaElement::canPlay() const 2878{ 2879 return paused() || ended() || m_readyState < HAVE_METADATA; 2880} 2881 2882double HTMLMediaElement::percentLoaded() const 2883{ 2884 if (!m_player) 2885 return 0; 2886 double duration = m_player->duration(); 2887 2888 if (!duration || std::isinf(duration)) 2889 return 0; 2890 2891 double buffered = 0; 2892 RefPtr<TimeRanges> timeRanges = m_player->buffered(); 2893 for (unsigned i = 0; i < timeRanges->length(); ++i) { 2894 double start = timeRanges->start(i, IGNORE_EXCEPTION); 2895 double end = timeRanges->end(i, IGNORE_EXCEPTION); 2896 buffered += end - start; 2897 } 2898 return buffered / duration; 2899} 2900 2901#if ENABLE(VIDEO_TRACK) 2902 2903void HTMLMediaElement::mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrackPrivate> prpTrack) 2904{ 2905 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 2906 return; 2907 2908 addAudioTrack(AudioTrack::create(this, prpTrack)); 2909} 2910 2911void HTMLMediaElement::mediaPlayerDidAddTextTrack(PassRefPtr<InbandTextTrackPrivate> prpTrack) 2912{ 2913 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 2914 return; 2915 2916 // 4.8.10.12.2 Sourcing in-band text tracks 2917 // 1. Associate the relevant data with a new text track and its corresponding new TextTrack object. 2918 RefPtr<InbandTextTrack> textTrack = InbandTextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, prpTrack); 2919 2920 // 2. Set the new text track's kind, label, and language based on the semantics of the relevant data, 2921 // as defined by the relevant specification. If there is no label in that data, then the label must 2922 // be set to the empty string. 2923 // 3. Associate the text track list of cues with the rules for updating the text track rendering appropriate 2924 // for the format in question. 2925 // 4. If the new text track's kind is metadata, then set the text track in-band metadata track dispatch type 2926 // as follows, based on the type of the media resource: 2927 // 5. Populate the new text track's list of cues with the cues parsed so far, folllowing the guidelines for exposing 2928 // cues, and begin updating it dynamically as necessary. 2929 // - Thess are all done by the media engine. 2930 2931 // 6. Set the new text track's readiness state to loaded. 2932 textTrack->setReadinessState(TextTrack::Loaded); 2933 2934 // 7. Set the new text track's mode to the mode consistent with the user's preferences and the requirements of 2935 // the relevant specification for the data. 2936 // - This will happen in configureTextTracks() 2937 scheduleDelayedAction(ConfigureTextTracks); 2938 2939 // 8. Add the new text track to the media element's list of text tracks. 2940 // 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent 2941 // interface, with the track attribute initialized to the text track's TextTrack object, at the media element's 2942 // textTracks attribute's TextTrackList object. 2943 addTextTrack(textTrack.release()); 2944} 2945 2946void HTMLMediaElement::mediaPlayerDidAddVideoTrack(PassRefPtr<VideoTrackPrivate> prpTrack) 2947{ 2948 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 2949 return; 2950 2951 addVideoTrack(VideoTrack::create(this, prpTrack)); 2952} 2953 2954void HTMLMediaElement::mediaPlayerDidRemoveAudioTrack(PassRefPtr<AudioTrackPrivate> prpTrack) 2955{ 2956 prpTrack->willBeRemoved(); 2957} 2958 2959void HTMLMediaElement::mediaPlayerDidRemoveTextTrack(PassRefPtr<InbandTextTrackPrivate> prpTrack) 2960{ 2961 prpTrack->willBeRemoved(); 2962} 2963 2964void HTMLMediaElement::mediaPlayerDidRemoveVideoTrack(PassRefPtr<VideoTrackPrivate> prpTrack) 2965{ 2966 prpTrack->willBeRemoved(); 2967} 2968 2969#if USE(PLATFORM_TEXT_TRACK_MENU) 2970void HTMLMediaElement::setSelectedTextTrack(PassRefPtr<PlatformTextTrack> platformTrack) 2971{ 2972 if (!m_textTracks) 2973 return; 2974 2975 TrackDisplayUpdateScope scope(this); 2976 2977 if (!platformTrack) { 2978 setSelectedTextTrack(0); 2979 return; 2980 } 2981 2982 TextTrack* textTrack; 2983 size_t i; 2984 for (i = 0; i < m_textTracks->length(); ++i) { 2985 textTrack = m_textTracks->item(i); 2986 2987 if (textTrack->platformTextTrack() == platformTrack) 2988 break; 2989 } 2990 2991 if (i == m_textTracks->length()) 2992 return; 2993 setSelectedTextTrack(textTrack); 2994} 2995 2996Vector<RefPtr<PlatformTextTrack> > HTMLMediaElement::platformTextTracks() 2997{ 2998 if (!m_textTracks || !m_textTracks->length()) 2999 return Vector<RefPtr<PlatformTextTrack> >(); 3000 3001 Vector<RefPtr<PlatformTextTrack> > platformTracks; 3002 for (size_t i = 0; i < m_textTracks->length(); ++i) 3003 platformTracks.append(m_textTracks->item(i)->platformTextTrack()); 3004 3005 return platformTracks; 3006} 3007 3008void HTMLMediaElement::notifyMediaPlayerOfTextTrackChanges() 3009{ 3010 if (!m_textTracks || !m_textTracks->length() || !platformTextTrackMenu()) 3011 return; 3012 3013 m_platformMenu->tracksDidChange(); 3014} 3015 3016PlatformTextTrackMenuInterface* HTMLMediaElement::platformTextTrackMenu() 3017{ 3018 if (m_platformMenu) 3019 return m_platformMenu.get(); 3020 3021 if (!m_player || !m_player->implementsTextTrackControls()) 3022 return 0; 3023 3024 m_platformMenu = m_player->textTrackMenu(); 3025 if (!m_platformMenu) 3026 return 0; 3027 3028 m_platformMenu->setClient(this); 3029 3030 return m_platformMenu.get(); 3031} 3032#endif // #if USE(PLATFORM_TEXT_TRACK_MENU) 3033 3034void HTMLMediaElement::closeCaptionTracksChanged() 3035{ 3036 if (hasMediaControls()) 3037 mediaControls()->closedCaptionTracksChanged(); 3038 3039#if USE(PLATFORM_TEXT_TRACK_MENU) 3040 if (m_player && m_player->implementsTextTrackControls()) 3041 scheduleDelayedAction(TextTrackChangesNotification); 3042#endif 3043} 3044 3045void HTMLMediaElement::addAudioTrack(PassRefPtr<AudioTrack> track) 3046{ 3047 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3048 return; 3049 3050 audioTracks()->append(track); 3051} 3052 3053void HTMLMediaElement::addTextTrack(PassRefPtr<TextTrack> track) 3054{ 3055 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3056 return; 3057 3058 textTracks()->append(track); 3059 3060 closeCaptionTracksChanged(); 3061} 3062 3063void HTMLMediaElement::addVideoTrack(PassRefPtr<VideoTrack> track) 3064{ 3065 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3066 return; 3067 3068 videoTracks()->append(track); 3069} 3070 3071void HTMLMediaElement::removeAudioTrack(AudioTrack* track) 3072{ 3073 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3074 return; 3075 3076 m_audioTracks->remove(track); 3077} 3078 3079void HTMLMediaElement::removeTextTrack(TextTrack* track) 3080{ 3081 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3082 return; 3083 3084 TrackDisplayUpdateScope scope(this); 3085 TextTrackCueList* cues = track->cues(); 3086 if (cues) 3087 textTrackRemoveCues(track, cues); 3088 track->clearClient(); 3089 m_textTracks->remove(track); 3090 3091 closeCaptionTracksChanged(); 3092} 3093 3094void HTMLMediaElement::removeVideoTrack(VideoTrack* track) 3095{ 3096 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3097 return; 3098 3099 m_videoTracks->remove(track); 3100} 3101 3102void HTMLMediaElement::removeAllInbandTracks() 3103{ 3104 while (m_audioTracks && m_audioTracks->length()) 3105 removeAudioTrack(m_audioTracks->lastItem()); 3106 3107 if (m_textTracks) { 3108 TrackDisplayUpdateScope scope(this); 3109 for (int i = m_textTracks->length() - 1; i >= 0; --i) { 3110 TextTrack* track = m_textTracks->item(i); 3111 3112 if (track->trackType() == TextTrack::InBand) 3113 removeTextTrack(track); 3114 } 3115 } 3116 3117 while (m_videoTracks && m_videoTracks->length()) 3118 removeVideoTrack(m_videoTracks->lastItem()); 3119} 3120 3121PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec) 3122{ 3123 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3124 return 0; 3125 3126 // 4.8.10.12.4 Text track API 3127 // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps: 3128 3129 // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps 3130 if (!TextTrack::isValidKindKeyword(kind)) { 3131 ec = SYNTAX_ERR; 3132 return 0; 3133 } 3134 3135 // 2. If the label argument was omitted, let label be the empty string. 3136 // 3. If the language argument was omitted, let language be the empty string. 3137 // 4. Create a new TextTrack object. 3138 3139 // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text 3140 // track label to label, its text track language to language... 3141 RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language); 3142 3143 // Note, due to side effects when changing track parameters, we have to 3144 // first append the track to the text track list. 3145 3146 // 6. Add the new text track to the media element's list of text tracks. 3147 addTextTrack(textTrack); 3148 3149 // ... its text track readiness state to the text track loaded state ... 3150 textTrack->setReadinessState(TextTrack::Loaded); 3151 3152 // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ... 3153 textTrack->setMode(TextTrack::hiddenKeyword()); 3154 3155 return textTrack.release(); 3156} 3157 3158AudioTrackList* HTMLMediaElement::audioTracks() 3159{ 3160 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3161 return 0; 3162 3163 if (!m_audioTracks) 3164 m_audioTracks = AudioTrackList::create(this, ActiveDOMObject::scriptExecutionContext()); 3165 3166 return m_audioTracks.get(); 3167} 3168 3169TextTrackList* HTMLMediaElement::textTracks() 3170{ 3171 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3172 return 0; 3173 3174 if (!m_textTracks) 3175 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext()); 3176 3177 return m_textTracks.get(); 3178} 3179 3180VideoTrackList* HTMLMediaElement::videoTracks() 3181{ 3182 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3183 return 0; 3184 3185 if (!m_videoTracks) 3186 m_videoTracks = VideoTrackList::create(this, ActiveDOMObject::scriptExecutionContext()); 3187 3188 return m_videoTracks.get(); 3189} 3190 3191void HTMLMediaElement::didAddTextTrack(HTMLTrackElement* trackElement) 3192{ 3193 ASSERT(trackElement->hasTagName(trackTag)); 3194 3195 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3196 return; 3197 3198 // 4.8.10.12.3 Sourcing out-of-band text tracks 3199 // When a track element's parent element changes and the new parent is a media element, 3200 // then the user agent must add the track element's corresponding text track to the 3201 // media element's list of text tracks ... [continues in TextTrackList::append] 3202 RefPtr<TextTrack> textTrack = trackElement->track(); 3203 if (!textTrack) 3204 return; 3205 3206 addTextTrack(textTrack.release()); 3207 3208 // Do not schedule the track loading until parsing finishes so we don't start before all tracks 3209 // in the markup have been added. 3210 if (!m_parsingInProgress) 3211 scheduleDelayedAction(ConfigureTextTracks); 3212 3213 if (hasMediaControls()) 3214 mediaControls()->closedCaptionTracksChanged(); 3215} 3216 3217void HTMLMediaElement::didRemoveTextTrack(HTMLTrackElement* trackElement) 3218{ 3219 ASSERT(trackElement->hasTagName(trackTag)); 3220 3221 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3222 return; 3223 3224#if !LOG_DISABLED 3225 if (trackElement->hasTagName(trackTag)) { 3226 KURL url = trackElement->getNonEmptyURLAttribute(srcAttr); 3227 LOG(Media, "HTMLMediaElement::didRemoveTrack - 'src' is %s", urlForLoggingMedia(url).utf8().data()); 3228 } 3229#endif 3230 3231 RefPtr<TextTrack> textTrack = trackElement->track(); 3232 if (!textTrack) 3233 return; 3234 3235 textTrack->setHasBeenConfigured(false); 3236 3237 if (!m_textTracks) 3238 return; 3239 3240 // 4.8.10.12.3 Sourcing out-of-band text tracks 3241 // When a track element's parent element changes and the old parent was a media element, 3242 // then the user agent must remove the track element's corresponding text track from the 3243 // media element's list of text tracks. 3244 removeTextTrack(textTrack.get()); 3245 3246 size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get()); 3247 if (index != notFound) 3248 m_textTracksWhenResourceSelectionBegan.remove(index); 3249} 3250 3251void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group) 3252{ 3253 ASSERT(group.tracks.size()); 3254 3255 LOG(Media, "HTMLMediaElement::configureTextTrackGroup"); 3256 3257 Page* page = document()->page(); 3258 CaptionUserPreferences* captionPreferences = page? page->group().captionPreferences() : 0; 3259 CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences ? captionPreferences->captionDisplayMode() : CaptionUserPreferences::Automatic; 3260 3261 // First, find the track in the group that should be enabled (if any). 3262 Vector<RefPtr<TextTrack> > currentlyEnabledTracks; 3263 RefPtr<TextTrack> trackToEnable; 3264 RefPtr<TextTrack> defaultTrack; 3265 RefPtr<TextTrack> fallbackTrack; 3266 RefPtr<TextTrack> forcedSubitleTrack; 3267 int highestTrackScore = 0; 3268 int highestForcedScore = 0; 3269 for (size_t i = 0; i < group.tracks.size(); ++i) { 3270 RefPtr<TextTrack> textTrack = group.tracks[i]; 3271 3272 if (m_processingPreferenceChange && textTrack->mode() == TextTrack::showingKeyword()) 3273 currentlyEnabledTracks.append(textTrack); 3274 3275 int trackScore = captionPreferences ? captionPreferences->textTrackSelectionScore(textTrack.get(), this) : 0; 3276 LOG(Media, "HTMLMediaElement::configureTextTrackGroup - '%s' track with language '%s' has score %i", textTrack->kind().string().utf8().data(), textTrack->language().string().utf8().data(), trackScore); 3277 3278 if (trackScore) { 3279 3280 // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a 3281 // track with this text track kind, text track language, and text track label enabled, and there is no 3282 // other text track in the media element's list of text tracks with a text track kind of either subtitles 3283 // or captions whose text track mode is showing 3284 // ... 3285 // * If the text track kind is chapters and the text track language is one that the user agent has reason 3286 // to believe is appropriate for the user, and there is no other text track in the media element's list of 3287 // text tracks with a text track kind of chapters whose text track mode is showing 3288 // Let the text track mode be showing. 3289 if (trackScore > highestTrackScore) { 3290 highestTrackScore = trackScore; 3291 trackToEnable = textTrack; 3292 } 3293 3294 if (!defaultTrack && textTrack->isDefault()) 3295 defaultTrack = textTrack; 3296 if (!defaultTrack && !fallbackTrack) 3297 fallbackTrack = textTrack; 3298 if (textTrack->containsOnlyForcedSubtitles() && trackScore > highestForcedScore) { 3299 forcedSubitleTrack = textTrack; 3300 highestForcedScore = trackScore; 3301 } 3302 } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) { 3303 // * If the track element has a default attribute specified, and there is no other text track in the media 3304 // element's list of text tracks whose text track mode is showing or showing by default 3305 // Let the text track mode be showing by default. 3306 if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly) 3307 defaultTrack = textTrack; 3308 } 3309 } 3310 3311 if (!trackToEnable && defaultTrack) 3312 trackToEnable = defaultTrack; 3313 3314 // If no track matches the user's preferred language, none was marked as 'default', and there is a forced subtitle track 3315 // in the same language as the language of the primary audio track, enable it. 3316 if (!trackToEnable && forcedSubitleTrack) 3317 trackToEnable = forcedSubitleTrack; 3318 3319 // If no track matches, don't disable an already visible track unless preferences say they all should be off. 3320 if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly) { 3321 if (!trackToEnable && !defaultTrack && group.visibleTrack) 3322 trackToEnable = group.visibleTrack; 3323 } 3324 3325 // If no track matches the user's preferred language and non was marked 'default', enable the first track 3326 // because the user has explicitly stated a preference for this kind of track. 3327 if (!trackToEnable && fallbackTrack) 3328 trackToEnable = fallbackTrack; 3329 3330 if (trackToEnable) 3331 m_subtitleTrackLanguage = trackToEnable->language(); 3332 else 3333 m_subtitleTrackLanguage = emptyString(); 3334 3335 if (currentlyEnabledTracks.size()) { 3336 for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) { 3337 RefPtr<TextTrack> textTrack = currentlyEnabledTracks[i]; 3338 if (textTrack != trackToEnable) 3339 textTrack->setMode(TextTrack::disabledKeyword()); 3340 } 3341 } 3342 3343 if (trackToEnable) 3344 trackToEnable->setMode(TextTrack::showingKeyword()); 3345 3346 m_processingPreferenceChange = false; 3347} 3348 3349void HTMLMediaElement::setSelectedTextTrack(TextTrack* trackToSelect) 3350{ 3351 TextTrackList* trackList = textTracks(); 3352 if (!trackList || !trackList->length()) 3353 return; 3354 3355 if (trackToSelect != TextTrack::captionMenuOffItem() && trackToSelect != TextTrack::captionMenuAutomaticItem()) { 3356 if (!trackList->contains(trackToSelect)) 3357 return; 3358 3359 for (int i = 0, length = trackList->length(); i < length; ++i) { 3360 TextTrack* track = trackList->item(i); 3361 if (!trackToSelect || track != trackToSelect) 3362 track->setMode(TextTrack::disabledKeyword()); 3363 else 3364 track->setMode(TextTrack::showingKeyword()); 3365 } 3366 } 3367 3368 CaptionUserPreferences* captionPreferences = document()->page() ? document()->page()->group().captionPreferences() : 0; 3369 if (!captionPreferences) 3370 return; 3371 3372 CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences->captionDisplayMode(); 3373 if (trackToSelect == TextTrack::captionMenuOffItem()) 3374 displayMode = CaptionUserPreferences::ForcedOnly; 3375 else if (trackToSelect == TextTrack::captionMenuAutomaticItem()) 3376 displayMode = CaptionUserPreferences::Automatic; 3377 else { 3378 displayMode = CaptionUserPreferences::AlwaysOn; 3379 if (trackToSelect->language().length()) 3380 captionPreferences->setPreferredLanguage(trackToSelect->language()); 3381 3382 // Set m_captionDisplayMode here so we don't reconfigure again when the preference changed notification comes through. 3383 m_captionDisplayMode = displayMode; 3384 } 3385 3386 captionPreferences->setCaptionDisplayMode(displayMode); 3387} 3388 3389void HTMLMediaElement::configureTextTracks() 3390{ 3391 TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles); 3392 TrackGroup descriptionTracks(TrackGroup::Description); 3393 TrackGroup chapterTracks(TrackGroup::Chapter); 3394 TrackGroup metadataTracks(TrackGroup::Metadata); 3395 TrackGroup otherTracks(TrackGroup::Other); 3396 3397 if (!m_textTracks) 3398 return; 3399 3400 for (size_t i = 0; i < m_textTracks->length(); ++i) { 3401 RefPtr<TextTrack> textTrack = m_textTracks->item(i); 3402 if (!textTrack) 3403 continue; 3404 3405 String kind = textTrack->kind(); 3406 TrackGroup* currentGroup; 3407 if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword() || kind == TextTrack::forcedKeyword()) 3408 currentGroup = &captionAndSubtitleTracks; 3409 else if (kind == TextTrack::descriptionsKeyword()) 3410 currentGroup = &descriptionTracks; 3411 else if (kind == TextTrack::chaptersKeyword()) 3412 currentGroup = &chapterTracks; 3413 else if (kind == TextTrack::metadataKeyword()) 3414 currentGroup = &metadataTracks; 3415 else 3416 currentGroup = &otherTracks; 3417 3418 if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword()) 3419 currentGroup->visibleTrack = textTrack; 3420 if (!currentGroup->defaultTrack && textTrack->isDefault()) 3421 currentGroup->defaultTrack = textTrack; 3422 3423 // Do not add this track to the group if it has already been automatically configured 3424 // as we only want to call configureTextTrack once per track so that adding another 3425 // track after the initial configuration doesn't reconfigure every track - only those 3426 // that should be changed by the new addition. For example all metadata tracks are 3427 // disabled by default, and we don't want a track that has been enabled by script 3428 // to be disabled automatically when a new metadata track is added later. 3429 if (textTrack->hasBeenConfigured()) 3430 continue; 3431 3432 if (textTrack->language().length()) 3433 currentGroup->hasSrcLang = true; 3434 currentGroup->tracks.append(textTrack); 3435 } 3436 3437 if (captionAndSubtitleTracks.tracks.size()) 3438 configureTextTrackGroup(captionAndSubtitleTracks); 3439 if (descriptionTracks.tracks.size()) 3440 configureTextTrackGroup(descriptionTracks); 3441 if (chapterTracks.tracks.size()) 3442 configureTextTrackGroup(chapterTracks); 3443 if (metadataTracks.tracks.size()) 3444 configureTextTrackGroup(metadataTracks); 3445 if (otherTracks.tracks.size()) 3446 configureTextTrackGroup(otherTracks); 3447 3448 configureTextTrackDisplay(); 3449 if (hasMediaControls()) 3450 mediaControls()->closedCaptionTracksChanged(); 3451} 3452#endif 3453 3454bool HTMLMediaElement::havePotentialSourceChild() 3455{ 3456 // Stash the current <source> node and next nodes so we can restore them after checking 3457 // to see there is another potential. 3458 RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode; 3459 RefPtr<Node> nextNode = m_nextChildNodeToConsider; 3460 3461 KURL nextURL = selectNextSourceChild(0, 0, DoNothing); 3462 3463 m_currentSourceNode = currentSourceNode; 3464 m_nextChildNodeToConsider = nextNode; 3465 3466 return nextURL.isValid(); 3467} 3468 3469KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid) 3470{ 3471#if !LOG_DISABLED 3472 // Don't log if this was just called to find out if there are any valid <source> elements. 3473 bool shouldLog = actionIfInvalid != DoNothing; 3474 if (shouldLog) 3475 LOG(Media, "HTMLMediaElement::selectNextSourceChild"); 3476#endif 3477 3478 if (!m_nextChildNodeToConsider) { 3479#if !LOG_DISABLED 3480 if (shouldLog) 3481 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\""); 3482#endif 3483 return KURL(); 3484 } 3485 3486 KURL mediaURL; 3487 Node* node; 3488 HTMLSourceElement* source = 0; 3489 String type; 3490 String system; 3491 bool lookingForStartNode = m_nextChildNodeToConsider; 3492 bool canUseSourceElement = false; 3493 bool okToLoadSourceURL; 3494 3495 NodeVector potentialSourceNodes; 3496 getChildNodes(this, potentialSourceNodes); 3497 3498 for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) { 3499 node = potentialSourceNodes[i].get(); 3500 if (lookingForStartNode && m_nextChildNodeToConsider != node) 3501 continue; 3502 lookingForStartNode = false; 3503 3504 if (!node->hasTagName(sourceTag)) 3505 continue; 3506 if (node->parentNode() != this) 3507 continue; 3508 3509 source = static_cast<HTMLSourceElement*>(node); 3510 3511 // 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 3512 mediaURL = source->getNonEmptyURLAttribute(srcAttr); 3513#if !LOG_DISABLED 3514 if (shouldLog) 3515 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLoggingMedia(mediaURL).utf8().data()); 3516#endif 3517 if (mediaURL.isEmpty()) 3518 goto check_again; 3519 3520 if (source->fastHasAttribute(mediaAttr)) { 3521 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); 3522 RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(source->media()); 3523#if !LOG_DISABLED 3524 if (shouldLog) 3525 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data()); 3526#endif 3527 if (!screenEval.eval(media.get())) 3528 goto check_again; 3529 } 3530 3531 type = source->type(); 3532 // FIXME(82965): Add support for keySystem in <source> and set system from source. 3533 if (type.isEmpty() && mediaURL.protocolIsData()) 3534 type = mimeTypeFromDataURL(mediaURL); 3535 if (!type.isEmpty() || !system.isEmpty()) { 3536#if !LOG_DISABLED 3537 if (shouldLog) 3538 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data()); 3539#endif 3540 if (!MediaPlayer::supportsType(ContentType(type), system, mediaURL, this)) 3541 goto check_again; 3542 } 3543 3544 // Is it safe to load this url? 3545 okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string()); 3546 3547 // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node. 3548 if (node->parentNode() != this) { 3549 LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element"); 3550 source = 0; 3551 goto check_again; 3552 } 3553 3554 if (!okToLoadSourceURL) 3555 goto check_again; 3556 3557 // Making it this far means the <source> looks reasonable. 3558 canUseSourceElement = true; 3559 3560check_again: 3561 if (!canUseSourceElement && actionIfInvalid == Complain && source) 3562 source->scheduleErrorEvent(); 3563 } 3564 3565 if (canUseSourceElement) { 3566 if (contentType) 3567 *contentType = ContentType(type); 3568 if (keySystem) 3569 *keySystem = system; 3570 m_currentSourceNode = source; 3571 m_nextChildNodeToConsider = source->nextSibling(); 3572 } else { 3573 m_currentSourceNode = 0; 3574 m_nextChildNodeToConsider = 0; 3575 } 3576 3577#if !LOG_DISABLED 3578 if (shouldLog) 3579 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLoggingMedia(mediaURL).utf8().data() : ""); 3580#endif 3581 return canUseSourceElement ? mediaURL : KURL(); 3582} 3583 3584void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source) 3585{ 3586 LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source); 3587 3588#if !LOG_DISABLED 3589 if (source->hasTagName(sourceTag)) { 3590 KURL url = source->getNonEmptyURLAttribute(srcAttr); 3591 LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLoggingMedia(url).utf8().data()); 3592 } 3593#endif 3594 3595 // We should only consider a <source> element when there is not src attribute at all. 3596 if (fastHasAttribute(srcAttr)) 3597 return; 3598 3599 // 4.8.8 - If a source element is inserted as a child of a media element that has no src 3600 // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke 3601 // the media element's resource selection algorithm. 3602 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) { 3603 scheduleDelayedAction(LoadMediaResource); 3604 m_nextChildNodeToConsider = source; 3605 return; 3606 } 3607 3608 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) { 3609 LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source"); 3610 m_nextChildNodeToConsider = source; 3611 return; 3612 } 3613 3614 if (m_nextChildNodeToConsider) 3615 return; 3616 3617 // 4.8.9.5, resource selection algorithm, source elements section: 3618 // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.) 3619 // 22. Asynchronously await a stable state... 3620 // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case 3621 // it hasn't been fired yet). 3622 setShouldDelayLoadEvent(true); 3623 3624 // 24. Set the networkState back to NETWORK_LOADING. 3625 m_networkState = NETWORK_LOADING; 3626 3627 // 25. Jump back to the find next candidate step above. 3628 m_nextChildNodeToConsider = source; 3629 scheduleNextSourceChild(); 3630} 3631 3632void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source) 3633{ 3634 LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source); 3635 3636#if !LOG_DISABLED 3637 if (source->hasTagName(sourceTag)) { 3638 KURL url = source->getNonEmptyURLAttribute(srcAttr); 3639 LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLoggingMedia(url).utf8().data()); 3640 } 3641#endif 3642 3643 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider) 3644 return; 3645 3646 if (source == m_nextChildNodeToConsider) { 3647 if (m_currentSourceNode) 3648 m_nextChildNodeToConsider = m_currentSourceNode->nextSibling(); 3649 LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get()); 3650 } else if (source == m_currentSourceNode) { 3651 // Clear the current source node pointer, but don't change the movie as the spec says: 3652 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already 3653 // inserted in a video or audio element will have no effect. 3654 m_currentSourceNode = 0; 3655 LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0"); 3656 } 3657} 3658 3659void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) 3660{ 3661 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged"); 3662 3663#if ENABLE(VIDEO_TRACK) 3664 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 3665 updateActiveTextTrackCues(currentTime()); 3666#endif 3667 3668 beginProcessingMediaPlayerCallback(); 3669 3670 invalidateCachedTime(); 3671 3672 // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek. 3673 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA && !m_player->seeking()) 3674 finishSeek(); 3675 3676 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, 3677 // it will only queue a 'timeupdate' event if we haven't already posted one at the current 3678 // movie time. 3679 scheduleTimeupdateEvent(false); 3680 3681 double now = currentTime(); 3682 double dur = duration(); 3683 3684 // When the current playback position reaches the end of the media resource when the direction of 3685 // playback is forwards, then the user agent must follow these steps: 3686 if (!std::isnan(dur) && dur && now >= dur && m_playbackRate > 0) { 3687 // If the media element has a loop attribute specified and does not have a current media controller, 3688 if (loop() && !m_mediaController) { 3689 m_sentEndEvent = false; 3690 // then seek to the earliest possible position of the media resource and abort these steps. 3691 seek(startTime(), IGNORE_EXCEPTION); 3692 } else { 3693 // If the media element does not have a current media controller, and the media element 3694 // has still ended playback, and the direction of playback is still forwards, and paused 3695 // is false, 3696 if (!m_mediaController && !m_paused) { 3697 // changes paused to true and fires a simple event named pause at the media element. 3698 m_paused = true; 3699 scheduleEvent(eventNames().pauseEvent); 3700 } 3701 // Queue a task to fire a simple event named ended at the media element. 3702 if (!m_sentEndEvent) { 3703 m_sentEndEvent = true; 3704 scheduleEvent(eventNames().endedEvent); 3705 } 3706 // If the media element has a current media controller, then report the controller state 3707 // for the media element's current media controller. 3708 updateMediaController(); 3709 } 3710 } 3711 else 3712 m_sentEndEvent = false; 3713 3714 updatePlayState(); 3715 endProcessingMediaPlayerCallback(); 3716} 3717 3718void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) 3719{ 3720 LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged"); 3721 3722 beginProcessingMediaPlayerCallback(); 3723 if (m_player) { 3724 double vol = m_player->volume(); 3725 if (vol != m_volume) { 3726 m_volume = vol; 3727 updateVolume(); 3728 scheduleEvent(eventNames().volumechangeEvent); 3729 } 3730 } 3731 endProcessingMediaPlayerCallback(); 3732} 3733 3734void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) 3735{ 3736 LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged"); 3737 3738 beginProcessingMediaPlayerCallback(); 3739 if (m_player) 3740 setMuted(m_player->muted()); 3741 endProcessingMediaPlayerCallback(); 3742} 3743 3744void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player) 3745{ 3746 LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged"); 3747 3748 beginProcessingMediaPlayerCallback(); 3749 3750 scheduleEvent(eventNames().durationchangeEvent); 3751 mediaPlayerCharacteristicChanged(player); 3752 3753 double now = currentTime(); 3754 double dur = duration(); 3755 if (now > dur) 3756 seek(dur, IGNORE_EXCEPTION); 3757 3758 endProcessingMediaPlayerCallback(); 3759} 3760 3761void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) 3762{ 3763 LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged"); 3764 3765 beginProcessingMediaPlayerCallback(); 3766 3767 // Stash the rate in case the one we tried to set isn't what the engine is 3768 // using (eg. it can't handle the rate we set) 3769 m_playbackRate = m_player->rate(); 3770 if (m_playing) 3771 invalidateCachedTime(); 3772 3773#if PLATFORM(MAC) 3774 updateDisableSleep(); 3775#endif 3776 3777 endProcessingMediaPlayerCallback(); 3778} 3779 3780void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*) 3781{ 3782 LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged"); 3783 3784 if (!m_player || m_pausedInternal) 3785 return; 3786 3787 beginProcessingMediaPlayerCallback(); 3788 if (m_player->paused()) 3789 pauseInternal(); 3790 else 3791 playInternal(); 3792 endProcessingMediaPlayerCallback(); 3793} 3794 3795void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) 3796{ 3797 LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks"); 3798 3799 // The MediaPlayer came across content it cannot completely handle. 3800 // This is normally acceptable except when we are in a standalone 3801 // MediaDocument. If so, tell the document what has happened. 3802 if (ownerDocument()->isMediaDocument()) { 3803 MediaDocument* mediaDocument = toMediaDocument(ownerDocument()); 3804 mediaDocument->mediaElementSawUnsupportedTracks(); 3805 } 3806} 3807 3808void HTMLMediaElement::mediaPlayerResourceNotSupported(MediaPlayer*) 3809{ 3810 LOG(Media, "HTMLMediaElement::mediaPlayerResourceNotSupported"); 3811 3812 // The MediaPlayer came across content which no installed engine supports. 3813 mediaLoadingFailed(MediaPlayer::FormatError); 3814} 3815 3816// MediaPlayerPresentation methods 3817void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) 3818{ 3819 beginProcessingMediaPlayerCallback(); 3820 updateDisplayState(); 3821 if (renderer()) 3822 renderer()->repaint(); 3823 endProcessingMediaPlayerCallback(); 3824} 3825 3826void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) 3827{ 3828 LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged"); 3829 3830 beginProcessingMediaPlayerCallback(); 3831 if (renderer()) 3832 renderer()->updateFromElement(); 3833 endProcessingMediaPlayerCallback(); 3834} 3835 3836#if USE(ACCELERATED_COMPOSITING) 3837bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) 3838{ 3839 if (renderer() && renderer()->isVideo()) { 3840 ASSERT(renderer()->view()); 3841 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer())); 3842 } 3843 return false; 3844} 3845 3846void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*) 3847{ 3848 LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged"); 3849 3850 // Kick off a fake recalcStyle that will update the compositing tree. 3851 setNeedsStyleRecalc(SyntheticStyleChange); 3852} 3853#endif 3854 3855#if PLATFORM(WIN) && USE(AVFOUNDATION) 3856GraphicsDeviceAdapter* HTMLMediaElement::mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const 3857{ 3858 if (!document() || !document()->page()) 3859 return 0; 3860 3861 return document()->page()->chrome().client()->graphicsDeviceAdapter(); 3862} 3863#endif 3864 3865void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*) 3866{ 3867 LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated"); 3868 beginProcessingMediaPlayerCallback(); 3869 if (renderer()) 3870 renderer()->updateFromElement(); 3871 endProcessingMediaPlayerCallback(); 3872} 3873 3874void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*) 3875{ 3876 LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable"); 3877 beginProcessingMediaPlayerCallback(); 3878 if (displayMode() == PosterWaitingForVideo) { 3879 setDisplayMode(Video); 3880#if USE(ACCELERATED_COMPOSITING) 3881 mediaPlayerRenderingModeChanged(m_player.get()); 3882#endif 3883 } 3884 endProcessingMediaPlayerCallback(); 3885} 3886 3887void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*) 3888{ 3889 LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged"); 3890 3891 beginProcessingMediaPlayerCallback(); 3892 3893#if ENABLE(VIDEO_TRACK) 3894 if (m_captionDisplayMode == CaptionUserPreferences::Automatic && m_subtitleTrackLanguage != m_player->languageOfPrimaryAudioTrack()) 3895 markCaptionAndSubtitleTracksAsUnconfigured(AfterDelay); 3896#endif 3897 3898 if (hasMediaControls()) 3899 mediaControls()->reset(); 3900 if (renderer()) 3901 renderer()->updateFromElement(); 3902 endProcessingMediaPlayerCallback(); 3903} 3904 3905PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const 3906{ 3907 if (!m_player) 3908 return TimeRanges::create(); 3909 return m_player->buffered(); 3910} 3911 3912PassRefPtr<TimeRanges> HTMLMediaElement::played() 3913{ 3914 if (m_playing) { 3915 double time = currentTime(); 3916 if (time > m_lastSeekTime) 3917 addPlayedRange(m_lastSeekTime, time); 3918 } 3919 3920 if (!m_playedTimeRanges) 3921 m_playedTimeRanges = TimeRanges::create(); 3922 3923 return m_playedTimeRanges->copy(); 3924} 3925 3926PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const 3927{ 3928 return m_player ? m_player->seekable() : TimeRanges::create(); 3929} 3930 3931bool HTMLMediaElement::potentiallyPlaying() const 3932{ 3933 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing 3934 // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the 3935 // checks in couldPlayIfEnoughData(). 3936 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA; 3937 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController(); 3938} 3939 3940bool HTMLMediaElement::couldPlayIfEnoughData() const 3941{ 3942 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); 3943} 3944 3945bool HTMLMediaElement::endedPlayback() const 3946{ 3947 double dur = duration(); 3948 if (!m_player || std::isnan(dur)) 3949 return false; 3950 3951 // 4.8.10.8 Playing the media resource 3952 3953 // A media element is said to have ended playback when the element's 3954 // readyState attribute is HAVE_METADATA or greater, 3955 if (m_readyState < HAVE_METADATA) 3956 return false; 3957 3958 // and the current playback position is the end of the media resource and the direction 3959 // of playback is forwards, Either the media element does not have a loop attribute specified, 3960 // or the media element has a current media controller. 3961 double now = currentTime(); 3962 if (m_playbackRate > 0) 3963 return dur > 0 && now >= dur && (!loop() || m_mediaController); 3964 3965 // or the current playback position is the earliest possible position and the direction 3966 // of playback is backwards 3967 if (m_playbackRate < 0) 3968 return now <= 0; 3969 3970 return false; 3971} 3972 3973bool HTMLMediaElement::stoppedDueToErrors() const 3974{ 3975 if (m_readyState >= HAVE_METADATA && m_error) { 3976 RefPtr<TimeRanges> seekableRanges = seekable(); 3977 if (!seekableRanges->contain(currentTime())) 3978 return true; 3979 } 3980 3981 return false; 3982} 3983 3984bool HTMLMediaElement::pausedForUserInteraction() const 3985{ 3986// return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] 3987 return false; 3988} 3989 3990double HTMLMediaElement::minTimeSeekable() const 3991{ 3992 return m_player ? m_player->minTimeSeekable() : 0; 3993} 3994 3995double HTMLMediaElement::maxTimeSeekable() const 3996{ 3997 return m_player ? m_player->maxTimeSeekable() : 0; 3998} 3999 4000void HTMLMediaElement::updateVolume() 4001{ 4002 if (!m_player) 4003 return; 4004 4005 // Avoid recursion when the player reports volume changes. 4006 if (!processingMediaPlayerCallback()) { 4007 Page* page = document()->page(); 4008 double volumeMultiplier = page ? page->mediaVolume() : 1; 4009 bool shouldMute = m_muted; 4010 4011 if (m_mediaController) { 4012 volumeMultiplier *= m_mediaController->volume(); 4013 shouldMute = m_mediaController->muted(); 4014 } 4015 4016 m_player->setMuted(shouldMute); 4017 m_player->setVolume(m_volume * volumeMultiplier); 4018 } 4019 4020 if (hasMediaControls()) 4021 mediaControls()->changedVolume(); 4022} 4023 4024void HTMLMediaElement::updatePlayState() 4025{ 4026 if (!m_player) 4027 return; 4028 4029 if (m_pausedInternal) { 4030 if (!m_player->paused()) 4031 m_player->pause(); 4032 refreshCachedTime(); 4033 m_playbackProgressTimer.stop(); 4034 if (hasMediaControls()) 4035 mediaControls()->playbackStopped(); 4036 m_activityToken = nullptr; 4037 return; 4038 } 4039 4040 bool shouldBePlaying = potentiallyPlaying(); 4041 bool playerPaused = m_player->paused(); 4042 4043 LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s", 4044 boolString(shouldBePlaying), boolString(playerPaused)); 4045 4046 if (shouldBePlaying) { 4047 setDisplayMode(Video); 4048 invalidateCachedTime(); 4049 4050 if (playerPaused) { 4051 if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome().requiresFullscreenForVideoPlayback()) 4052 enterFullscreen(); 4053 4054 // Set rate, muted before calling play in case they were set before the media engine was setup. 4055 // The media engine should just stash the rate and muted values since it isn't already playing. 4056 m_player->setRate(m_playbackRate); 4057 m_player->setMuted(m_muted); 4058 4059 m_player->play(); 4060 } 4061 4062 if (hasMediaControls()) 4063 mediaControls()->playbackStarted(); 4064 if (document()->page()) 4065 m_activityToken = document()->page()->createActivityToken(); 4066 4067 startPlaybackProgressTimer(); 4068 m_playing = true; 4069 4070 } else { // Should not be playing right now 4071 if (!playerPaused) 4072 m_player->pause(); 4073 refreshCachedTime(); 4074 4075 m_playbackProgressTimer.stop(); 4076 m_playing = false; 4077 double time = currentTime(); 4078 if (time > m_lastSeekTime) 4079 addPlayedRange(m_lastSeekTime, time); 4080 4081 if (couldPlayIfEnoughData()) 4082 prepareToPlay(); 4083 4084 if (hasMediaControls()) 4085 mediaControls()->playbackStopped(); 4086 m_activityToken = nullptr; 4087 } 4088 4089 updateMediaController(); 4090 4091 if (renderer()) 4092 renderer()->updateFromElement(); 4093} 4094 4095void HTMLMediaElement::setPausedInternal(bool b) 4096{ 4097 m_pausedInternal = b; 4098 updatePlayState(); 4099} 4100 4101void HTMLMediaElement::stopPeriodicTimers() 4102{ 4103 m_progressEventTimer.stop(); 4104 m_playbackProgressTimer.stop(); 4105} 4106 4107void HTMLMediaElement::userCancelledLoad() 4108{ 4109 LOG(Media, "HTMLMediaElement::userCancelledLoad"); 4110 4111 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded) 4112 return; 4113 4114 // If the media data fetching process is aborted by the user: 4115 4116 // 1 - The user agent should cancel the fetching process. 4117 clearMediaPlayer(-1); 4118 4119 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED. 4120 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); 4121 4122 // 3 - Queue a task to fire a simple event named error at the media element. 4123 scheduleEvent(eventNames().abortEvent); 4124 4125#if ENABLE(MEDIA_SOURCE) 4126 setSourceState(MediaSource::closedKeyword()); 4127#endif 4128 4129 // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the 4130 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a 4131 // simple event named emptied at the element. Otherwise, set the element's networkState 4132 // attribute to the NETWORK_IDLE value. 4133 if (m_readyState == HAVE_NOTHING) { 4134 m_networkState = NETWORK_EMPTY; 4135 scheduleEvent(eventNames().emptiedEvent); 4136 } 4137 else 4138 m_networkState = NETWORK_IDLE; 4139 4140 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 4141 setShouldDelayLoadEvent(false); 4142 4143 // 6 - Abort the overall resource selection algorithm. 4144 m_currentSourceNode = 0; 4145 4146 // Reset m_readyState since m_player is gone. 4147 m_readyState = HAVE_NOTHING; 4148 updateMediaController(); 4149#if ENABLE(VIDEO_TRACK) 4150 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) 4151 updateActiveTextTrackCues(0); 4152#endif 4153} 4154 4155void HTMLMediaElement::clearMediaPlayer(int flags) 4156{ 4157#if USE(PLATFORM_TEXT_TRACK_MENU) 4158 if (platformTextTrackMenu()) { 4159 m_platformMenu->setClient(0); 4160 m_platformMenu = 0; 4161 } 4162#endif 4163 4164#if ENABLE(VIDEO_TRACK) 4165 removeAllInbandTracks(); 4166#endif 4167 4168#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 4169 4170#if ENABLE(MEDIA_SOURCE) 4171 setSourceState(MediaSource::closedKeyword()); 4172#endif 4173 4174 m_player.clear(); 4175#endif 4176 stopPeriodicTimers(); 4177 m_loadTimer.stop(); 4178 4179 clearFlags(m_pendingActionFlags, flags); 4180 m_loadState = WaitingForSource; 4181 4182#if ENABLE(VIDEO_TRACK) 4183 if (m_textTracks) 4184 configureTextTrackDisplay(); 4185#endif 4186 4187#if PLATFORM(MAC) 4188 updateDisableSleep(); 4189#endif 4190} 4191 4192bool HTMLMediaElement::canSuspend() const 4193{ 4194 return true; 4195} 4196 4197void HTMLMediaElement::stop() 4198{ 4199 LOG(Media, "HTMLMediaElement::stop"); 4200 if (m_isFullscreen) 4201 exitFullscreen(); 4202 4203 m_inActiveDocument = false; 4204 userCancelledLoad(); 4205 4206 // Stop the playback without generating events 4207 m_playing = false; 4208 setPausedInternal(true); 4209 4210 if (renderer()) 4211 renderer()->updateFromElement(); 4212 4213 stopPeriodicTimers(); 4214 cancelPendingEventsAndCallbacks(); 4215 4216 m_asyncEventQueue->close(); 4217 4218#if PLATFORM(MAC) 4219 updateDisableSleep(); 4220#endif 4221} 4222 4223void HTMLMediaElement::suspend(ReasonForSuspension why) 4224{ 4225 LOG(Media, "HTMLMediaElement::suspend"); 4226 4227 switch (why) 4228 { 4229 case DocumentWillBecomeInactive: 4230 stop(); 4231 addBehaviorRestriction(RequirePageConsentToResumeMediaRestriction); 4232 break; 4233 case PageWillBeSuspended: 4234 case JavaScriptDebuggerPaused: 4235 case WillDeferLoading: 4236 // Do nothing, we don't pause media playback in these cases. 4237 break; 4238 } 4239} 4240 4241void HTMLMediaElement::resume() 4242{ 4243 LOG(Media, "HTMLMediaElement::resume"); 4244 4245 m_inActiveDocument = true; 4246 4247 Page* page = document()->page(); 4248 if (pageConsentRequiredForResume() && page && !page->canStartMedia()) 4249 document()->addMediaCanStartListener(this); 4250 else 4251 setPausedInternal(false); 4252 4253 removeBehaviorRestriction(RequirePageConsentToResumeMediaRestriction); 4254 4255 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { 4256 // Restart the load if it was aborted in the middle by moving the document to the page cache. 4257 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to 4258 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). 4259 // This behavior is not specified but it seems like a sensible thing to do. 4260 // As it is not safe to immedately start loading now, let's schedule a load. 4261 scheduleDelayedAction(LoadMediaResource); 4262 } 4263 4264 if (renderer()) 4265 renderer()->updateFromElement(); 4266} 4267 4268bool HTMLMediaElement::hasPendingActivity() const 4269{ 4270 return (hasAudio() && isPlaying()) || m_asyncEventQueue->hasPendingEvents(); 4271} 4272 4273void HTMLMediaElement::mediaVolumeDidChange() 4274{ 4275 LOG(Media, "HTMLMediaElement::mediaVolumeDidChange"); 4276 updateVolume(); 4277} 4278 4279void HTMLMediaElement::defaultEventHandler(Event* event) 4280{ 4281#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 4282 RenderObject* r = renderer(); 4283 if (!r || !r->isWidget()) 4284 return; 4285 4286 Widget* widget = toRenderWidget(r)->widget(); 4287 if (widget) 4288 widget->handleEvent(event); 4289#else 4290 HTMLElement::defaultEventHandler(event); 4291#endif 4292} 4293 4294bool HTMLMediaElement::willRespondToMouseClickEvents() 4295{ 4296#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 4297 return true; 4298#else 4299 return HTMLElement::willRespondToMouseClickEvents(); 4300#endif 4301} 4302 4303#if ENABLE(VIDEO_TRACK) 4304bool HTMLMediaElement::requiresTextTrackRepresentation() const 4305{ 4306 return m_player ? m_player->requiresTextTrackRepresentation() : 0; 4307} 4308 4309void HTMLMediaElement::setTextTrackRepresentation(TextTrackRepresentation* representation) 4310{ 4311 if (m_player) 4312 m_player->setTextTrackRepresentation(representation); 4313} 4314#endif // ENABLE(VIDEO_TRACK) 4315 4316#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 4317 4318void HTMLMediaElement::ensureMediaPlayer() 4319{ 4320 if (!m_player) 4321 createMediaPlayer(); 4322} 4323 4324void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) 4325{ 4326 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { 4327 togglePlayState(); 4328 return; 4329 } 4330 4331 if (m_player) 4332 m_player->deliverNotification(notification); 4333} 4334 4335void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) 4336{ 4337 ensureMediaPlayer(); 4338 m_player->setMediaPlayerProxy(proxy); 4339} 4340 4341void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values) 4342{ 4343 RefPtr<HTMLMediaElement> protect(this); // selectNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations. 4344 4345 Frame* frame = document()->frame(); 4346 4347 if (isVideo()) { 4348 HTMLVideoElement* video = static_cast<HTMLVideoElement*>(this); 4349 KURL posterURL = video->posterImageURL(); 4350 if (!posterURL.isEmpty() && frame && frame->loader()->willLoadMediaElementURL(posterURL)) { 4351 names.append(ASCIILiteral("_media_element_poster_")); 4352 values.append(posterURL.string()); 4353 } 4354 } 4355 4356 if (controls()) { 4357 names.append(ASCIILiteral("_media_element_controls_")); 4358 values.append(ASCIILiteral("true")); 4359 } 4360 4361 url = src(); 4362 if (!isSafeToLoadURL(url, Complain)) 4363 url = selectNextSourceChild(0, 0, DoNothing); 4364 4365 m_currentSrc = url; 4366 if (url.isValid() && frame && frame->loader()->willLoadMediaElementURL(url)) { 4367 names.append(ASCIILiteral("_media_element_src_")); 4368 values.append(m_currentSrc.string()); 4369 } 4370} 4371 4372void HTMLMediaElement::createMediaPlayerProxy() 4373{ 4374 ensureMediaPlayer(); 4375 4376 if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate)) 4377 return; 4378 4379 Frame* frame = document()->frame(); 4380 if (!frame) 4381 return; 4382 4383 LOG(Media, "HTMLMediaElement::createMediaPlayerProxy"); 4384 4385 KURL url; 4386 Vector<String> paramNames; 4387 Vector<String> paramValues; 4388 4389 getPluginProxyParams(url, paramNames, paramValues); 4390 4391 // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to 4392 // display:none 4393 m_proxyWidget = frame->loader()->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues); 4394 if (m_proxyWidget) 4395 m_needWidgetUpdate = false; 4396} 4397 4398void HTMLMediaElement::updateWidget(PluginCreationOption) 4399{ 4400 mediaElement->setNeedWidgetUpdate(false); 4401 4402 Vector<String> paramNames; 4403 Vector<String> paramValues; 4404 // FIXME: Rename kurl to something more sensible. 4405 KURL kurl; 4406 4407 mediaElement->getPluginProxyParams(kurl, paramNames, paramValues); 4408 // FIXME: What if document()->frame() is 0? 4409 SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); 4410 loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues); 4411} 4412 4413#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) 4414 4415bool HTMLMediaElement::isFullscreen() const 4416{ 4417 if (m_isFullscreen) 4418 return true; 4419 4420#if ENABLE(FULLSCREEN_API) 4421 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) 4422 return true; 4423#endif 4424 4425 return false; 4426} 4427 4428void HTMLMediaElement::toggleFullscreenState() 4429{ 4430 LOG(Media, "HTMLMediaElement::toggleFullscreenState - isFullscreen() is %s", boolString(isFullscreen())); 4431 4432 if (isFullscreen()) 4433 exitFullscreen(); 4434 else 4435 enterFullscreen(); 4436} 4437 4438void HTMLMediaElement::enterFullscreen() 4439{ 4440 LOG(Media, "HTMLMediaElement::enterFullscreen"); 4441 4442#if ENABLE(FULLSCREEN_API) 4443 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { 4444 document()->requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFullScreenRequirement); 4445 return; 4446 } 4447#endif 4448 ASSERT(!m_isFullscreen); 4449 m_isFullscreen = true; 4450 if (hasMediaControls()) 4451 mediaControls()->enteredFullscreen(); 4452 if (document() && document()->page()) { 4453 document()->page()->chrome().client()->enterFullscreenForNode(this); 4454 scheduleEvent(eventNames().webkitbeginfullscreenEvent); 4455 } 4456} 4457 4458void HTMLMediaElement::exitFullscreen() 4459{ 4460 LOG(Media, "HTMLMediaElement::exitFullscreen"); 4461 4462#if ENABLE(FULLSCREEN_API) 4463 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { 4464 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) 4465 document()->webkitCancelFullScreen(); 4466 return; 4467 } 4468#endif 4469 ASSERT(m_isFullscreen); 4470 m_isFullscreen = false; 4471 if (hasMediaControls()) 4472 mediaControls()->exitedFullscreen(); 4473 if (document() && document()->page()) { 4474 if (document()->page()->chrome().requiresFullscreenForVideoPlayback()) 4475 pauseInternal(); 4476 document()->page()->chrome().client()->exitFullscreenForNode(this); 4477 scheduleEvent(eventNames().webkitendfullscreenEvent); 4478 } 4479} 4480 4481void HTMLMediaElement::didBecomeFullscreenElement() 4482{ 4483 if (hasMediaControls()) 4484 mediaControls()->enteredFullscreen(); 4485} 4486 4487void HTMLMediaElement::willStopBeingFullscreenElement() 4488{ 4489 if (hasMediaControls()) 4490 mediaControls()->exitedFullscreen(); 4491} 4492 4493PlatformMedia HTMLMediaElement::platformMedia() const 4494{ 4495 return m_player ? m_player->platformMedia() : NoPlatformMedia; 4496} 4497 4498#if USE(ACCELERATED_COMPOSITING) 4499PlatformLayer* HTMLMediaElement::platformLayer() const 4500{ 4501 return m_player ? m_player->platformLayer() : 0; 4502} 4503#endif 4504 4505bool HTMLMediaElement::hasClosedCaptions() const 4506{ 4507 if (m_player && m_player->hasClosedCaptions()) 4508 return true; 4509 4510#if ENABLE(VIDEO_TRACK) 4511 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled() || !m_textTracks) 4512 return false; 4513 4514 for (unsigned i = 0; i < m_textTracks->length(); ++i) { 4515 if (m_textTracks->item(i)->readinessState() == TextTrack::FailedToLoad) 4516 continue; 4517 4518 if (m_textTracks->item(i)->kind() == TextTrack::captionsKeyword() 4519 || m_textTracks->item(i)->kind() == TextTrack::subtitlesKeyword()) 4520 return true; 4521 } 4522#endif 4523 4524 return false; 4525} 4526 4527bool HTMLMediaElement::closedCaptionsVisible() const 4528{ 4529 return m_closedCaptionsVisible; 4530} 4531 4532#if ENABLE(VIDEO_TRACK) 4533void HTMLMediaElement::updateTextTrackDisplay() 4534{ 4535 if (!hasMediaControls() && !createMediaControls()) 4536 return; 4537 4538 mediaControls()->updateTextTrackDisplay(); 4539} 4540#endif 4541 4542void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) 4543{ 4544 LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible)); 4545 4546 m_closedCaptionsVisible = false; 4547 4548 if (!m_player || !hasClosedCaptions()) 4549 return; 4550 4551 m_closedCaptionsVisible = closedCaptionVisible; 4552 m_player->setClosedCaptionsVisible(closedCaptionVisible); 4553 4554#if ENABLE(VIDEO_TRACK) 4555 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) { 4556 markCaptionAndSubtitleTracksAsUnconfigured(Immediately); 4557 updateTextTrackDisplay(); 4558 } 4559#else 4560 if (hasMediaControls()) 4561 mediaControls()->changedClosedCaptionsVisibility(); 4562#endif 4563} 4564 4565void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible) 4566{ 4567 m_webkitLegacyClosedCaptionOverride = visible; 4568 setClosedCaptionsVisible(visible); 4569} 4570 4571bool HTMLMediaElement::webkitClosedCaptionsVisible() const 4572{ 4573 return m_webkitLegacyClosedCaptionOverride && m_closedCaptionsVisible; 4574} 4575 4576 4577bool HTMLMediaElement::webkitHasClosedCaptions() const 4578{ 4579 return hasClosedCaptions(); 4580} 4581 4582#if ENABLE(MEDIA_STATISTICS) 4583unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const 4584{ 4585 if (!m_player) 4586 return 0; 4587 return m_player->audioDecodedByteCount(); 4588} 4589 4590unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const 4591{ 4592 if (!m_player) 4593 return 0; 4594 return m_player->videoDecodedByteCount(); 4595} 4596#endif 4597 4598void HTMLMediaElement::mediaCanStart() 4599{ 4600 LOG(Media, "HTMLMediaElement::mediaCanStart"); 4601 4602 ASSERT(m_isWaitingUntilMediaCanStart || m_pausedInternal); 4603 if (m_isWaitingUntilMediaCanStart) { 4604 m_isWaitingUntilMediaCanStart = false; 4605 loadInternal(); 4606 } 4607 if (m_pausedInternal) 4608 setPausedInternal(false); 4609} 4610 4611bool HTMLMediaElement::isURLAttribute(const Attribute& attribute) const 4612{ 4613 return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute); 4614} 4615 4616void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay) 4617{ 4618 if (m_shouldDelayLoadEvent == shouldDelay) 4619 return; 4620 4621 LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay)); 4622 4623 m_shouldDelayLoadEvent = shouldDelay; 4624 if (shouldDelay) 4625 document()->incrementLoadEventDelayCount(); 4626 else 4627 document()->decrementLoadEventDelayCount(); 4628} 4629 4630 4631void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites) 4632{ 4633 MediaPlayer::getSitesInMediaCache(sites); 4634} 4635 4636void HTMLMediaElement::clearMediaCache() 4637{ 4638 MediaPlayer::clearMediaCache(); 4639} 4640 4641void HTMLMediaElement::clearMediaCacheForSite(const String& site) 4642{ 4643 MediaPlayer::clearMediaCacheForSite(site); 4644} 4645 4646void HTMLMediaElement::resetMediaEngines() 4647{ 4648 MediaPlayer::resetMediaEngines(); 4649} 4650 4651void HTMLMediaElement::privateBrowsingStateDidChange() 4652{ 4653 if (!m_player) 4654 return; 4655 4656 Settings* settings = document()->settings(); 4657 bool privateMode = !settings || settings->privateBrowsingEnabled(); 4658 LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode)); 4659 m_player->setPrivateBrowsingMode(privateMode); 4660} 4661 4662MediaControls* HTMLMediaElement::mediaControls() const 4663{ 4664 return toMediaControls(userAgentShadowRoot()->firstChild()); 4665} 4666 4667bool HTMLMediaElement::hasMediaControls() const 4668{ 4669 if (ShadowRoot* userAgent = userAgentShadowRoot()) { 4670 Node* node = userAgent->firstChild(); 4671 ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isMediaControls()); 4672 return node; 4673 } 4674 4675 return false; 4676} 4677 4678bool HTMLMediaElement::createMediaControls() 4679{ 4680 if (hasMediaControls()) 4681 return true; 4682 4683 RefPtr<MediaControls> mediaControls = MediaControls::create(document()); 4684 if (!mediaControls) 4685 return false; 4686 4687 mediaControls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this)); 4688 mediaControls->reset(); 4689 if (isFullscreen()) 4690 mediaControls->enteredFullscreen(); 4691 4692 ensureUserAgentShadowRoot()->appendChild(mediaControls, ASSERT_NO_EXCEPTION); 4693 4694 if (!controls() || !inDocument()) 4695 mediaControls->hide(); 4696 4697 return true; 4698} 4699 4700void HTMLMediaElement::configureMediaControls() 4701{ 4702#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 4703 if (!controls() || !inDocument()) { 4704 if (hasMediaControls()) 4705 mediaControls()->hide(); 4706 return; 4707 } 4708 4709 if (!hasMediaControls() && !createMediaControls()) 4710 return; 4711 4712 mediaControls()->show(); 4713#else 4714 if (m_player) 4715 m_player->setControls(controls()); 4716 4717 if (!hasMediaControls() && inDocument()) 4718 createMediaControls(); 4719#endif 4720} 4721 4722#if ENABLE(VIDEO_TRACK) 4723void HTMLMediaElement::configureTextTrackDisplay() 4724{ 4725 ASSERT(m_textTracks); 4726 4727 if (m_processingPreferenceChange) 4728 return; 4729 4730 LOG(Media, "HTMLMediaElement::configureTextTrackDisplay"); 4731 4732 bool haveVisibleTextTrack = false; 4733 for (unsigned i = 0; i < m_textTracks->length(); ++i) { 4734 if (m_textTracks->item(i)->mode() == TextTrack::showingKeyword()) { 4735 haveVisibleTextTrack = true; 4736 break; 4737 } 4738 } 4739 4740 if (m_haveVisibleTextTrack == haveVisibleTextTrack) { 4741 updateActiveTextTrackCues(currentTime()); 4742 return; 4743 } 4744 4745 m_haveVisibleTextTrack = haveVisibleTextTrack; 4746 m_closedCaptionsVisible = m_haveVisibleTextTrack; 4747 4748 if (!m_haveVisibleTextTrack && !hasMediaControls()) 4749 return; 4750 if (!hasMediaControls() && !createMediaControls()) 4751 return; 4752 4753 mediaControls()->changedClosedCaptionsVisibility(); 4754 4755 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) { 4756 updateTextTrackDisplay(); 4757 updateActiveTextTrackCues(currentTime()); 4758 } 4759} 4760 4761void HTMLMediaElement::captionPreferencesChanged() 4762{ 4763 if (!isVideo()) 4764 return; 4765 4766 if (hasMediaControls()) 4767 mediaControls()->textTrackPreferencesChanged(); 4768 4769 if (!document()->page()) 4770 return; 4771 4772 CaptionUserPreferences::CaptionDisplayMode displayMode = document()->page()->group().captionPreferences()->captionDisplayMode(); 4773 if (m_captionDisplayMode == displayMode) 4774 return; 4775 4776 m_captionDisplayMode = displayMode; 4777 setClosedCaptionsVisible(m_captionDisplayMode == CaptionUserPreferences::AlwaysOn); 4778} 4779 4780void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured(ReconfigureMode mode) 4781{ 4782 if (!m_textTracks) 4783 return; 4784 4785 LOG(Media, "HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured"); 4786 4787 // Mark all tracks as not "configured" so that configureTextTracks() 4788 // will reconsider which tracks to display in light of new user preferences 4789 // (e.g. default tracks should not be displayed if the user has turned off 4790 // captions and non-default tracks should be displayed based on language 4791 // preferences if the user has turned captions on). 4792 for (unsigned i = 0; i < m_textTracks->length(); ++i) { 4793 4794 RefPtr<TextTrack> textTrack = m_textTracks->item(i); 4795 String kind = textTrack->kind(); 4796 4797 if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) 4798 textTrack->setHasBeenConfigured(false); 4799 } 4800 4801 m_processingPreferenceChange = true; 4802 clearFlags(m_pendingActionFlags, ConfigureTextTracks); 4803 if (mode == Immediately) 4804 configureTextTracks(); 4805 else 4806 scheduleDelayedAction(ConfigureTextTracks); 4807} 4808 4809#endif 4810 4811void* HTMLMediaElement::preDispatchEventHandler(Event* event) 4812{ 4813 if (event && event->type() == eventNames().webkitfullscreenchangeEvent) 4814 configureMediaControls(); 4815 4816 return 0; 4817} 4818 4819void HTMLMediaElement::createMediaPlayer() 4820{ 4821#if ENABLE(WEB_AUDIO) 4822 if (m_audioSourceNode) 4823 m_audioSourceNode->lock(); 4824#endif 4825 4826#if ENABLE(MEDIA_SOURCE) 4827 if (m_mediaSource) 4828 m_mediaSource->setReadyState(MediaSource::closedKeyword()); 4829#endif 4830 4831 m_player = MediaPlayer::create(this); 4832 4833#if ENABLE(WEB_AUDIO) 4834 if (m_audioSourceNode) { 4835 // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode. 4836 if (audioSourceProvider()) 4837 audioSourceProvider()->setClient(m_audioSourceNode); 4838 4839 m_audioSourceNode->unlock(); 4840 } 4841#endif 4842} 4843 4844#if ENABLE(WEB_AUDIO) 4845void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode) 4846{ 4847 m_audioSourceNode = sourceNode; 4848 4849 if (audioSourceProvider()) 4850 audioSourceProvider()->setClient(m_audioSourceNode); 4851} 4852 4853AudioSourceProvider* HTMLMediaElement::audioSourceProvider() 4854{ 4855 if (m_player) 4856 return m_player->audioSourceProvider(); 4857 4858 return 0; 4859} 4860#endif 4861 4862#if ENABLE(MICRODATA) 4863String HTMLMediaElement::itemValueText() const 4864{ 4865 return getURLAttribute(srcAttr); 4866} 4867 4868void HTMLMediaElement::setItemValueText(const String& value, ExceptionCode&) 4869{ 4870 setAttribute(srcAttr, value); 4871} 4872#endif 4873 4874const String& HTMLMediaElement::mediaGroup() const 4875{ 4876 return m_mediaGroup; 4877} 4878 4879void HTMLMediaElement::setMediaGroup(const String& group) 4880{ 4881 if (m_mediaGroup == group) 4882 return; 4883 m_mediaGroup = group; 4884 4885 // When a media element is created with a mediagroup attribute, and when a media element's mediagroup 4886 // attribute is set, changed, or removed, the user agent must run the following steps: 4887 // 1. Let m [this] be the media element in question. 4888 // 2. Let m have no current media controller, if it currently has one. 4889 setController(0); 4890 4891 // 3. If m's mediagroup attribute is being removed, then abort these steps. 4892 if (group.isNull() || group.isEmpty()) 4893 return; 4894 4895 // 4. If there is another media element whose Document is the same as m's Document (even if one or both 4896 // of these elements are not actually in the Document), 4897 HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(document()); 4898 for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) { 4899 if (*i == this) 4900 continue; 4901 4902 // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as 4903 // the new value of m's mediagroup attribute, 4904 if ((*i)->mediaGroup() == group) { 4905 // then let controller be that media element's current media controller. 4906 setController((*i)->controller()); 4907 return; 4908 } 4909 } 4910 4911 // Otherwise, let controller be a newly created MediaController. 4912 setController(MediaController::create(Node::scriptExecutionContext())); 4913} 4914 4915MediaController* HTMLMediaElement::controller() const 4916{ 4917 return m_mediaController.get(); 4918} 4919 4920void HTMLMediaElement::setController(PassRefPtr<MediaController> controller) 4921{ 4922 if (m_mediaController) 4923 m_mediaController->removeMediaElement(this); 4924 4925 m_mediaController = controller; 4926 4927 if (m_mediaController) 4928 m_mediaController->addMediaElement(this); 4929 4930 if (hasMediaControls()) 4931 mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this)); 4932} 4933 4934void HTMLMediaElement::updateMediaController() 4935{ 4936 if (m_mediaController) 4937 m_mediaController->reportControllerState(); 4938} 4939 4940bool HTMLMediaElement::dispatchEvent(PassRefPtr<Event> event) 4941{ 4942 bool dispatchResult; 4943 bool isCanPlayEvent; 4944 4945 isCanPlayEvent = (event->type() == eventNames().canplayEvent); 4946 4947 if (isCanPlayEvent) 4948 m_dispatchingCanPlayEvent = true; 4949 4950 dispatchResult = HTMLElement::dispatchEvent(event); 4951 4952 if (isCanPlayEvent) 4953 m_dispatchingCanPlayEvent = false; 4954 4955 return dispatchResult; 4956} 4957 4958bool HTMLMediaElement::isBlocked() const 4959{ 4960 // A media element is a blocked media element if its readyState attribute is in the 4961 // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state, 4962 if (m_readyState <= HAVE_CURRENT_DATA) 4963 return true; 4964 4965 // or if the element has paused for user interaction. 4966 return pausedForUserInteraction(); 4967} 4968 4969bool HTMLMediaElement::isBlockedOnMediaController() const 4970{ 4971 if (!m_mediaController) 4972 return false; 4973 4974 // A media element is blocked on its media controller if the MediaController is a blocked 4975 // media controller, 4976 if (m_mediaController->isBlocked()) 4977 return true; 4978 4979 // or if its media controller position is either before the media resource's earliest possible 4980 // position relative to the MediaController's timeline or after the end of the media resource 4981 // relative to the MediaController's timeline. 4982 double mediaControllerPosition = m_mediaController->currentTime(); 4983 if (mediaControllerPosition < startTime() || mediaControllerPosition > startTime() + duration()) 4984 return true; 4985 4986 return false; 4987} 4988 4989void HTMLMediaElement::prepareMediaFragmentURI() 4990{ 4991 MediaFragmentURIParser fragmentParser(m_currentSrc); 4992 double dur = duration(); 4993 4994 double start = fragmentParser.startTime(); 4995 if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) { 4996 m_fragmentStartTime = start; 4997 if (m_fragmentStartTime > dur) 4998 m_fragmentStartTime = dur; 4999 } else 5000 m_fragmentStartTime = MediaPlayer::invalidTime(); 5001 5002 double end = fragmentParser.endTime(); 5003 if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) { 5004 m_fragmentEndTime = end; 5005 if (m_fragmentEndTime > dur) 5006 m_fragmentEndTime = dur; 5007 } else 5008 m_fragmentEndTime = MediaPlayer::invalidTime(); 5009 5010 if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA) 5011 prepareToPlay(); 5012} 5013 5014void HTMLMediaElement::applyMediaFragmentURI() 5015{ 5016 if (m_fragmentStartTime != MediaPlayer::invalidTime()) { 5017 m_sentEndEvent = false; 5018 seek(m_fragmentStartTime, IGNORE_EXCEPTION); 5019 } 5020} 5021 5022#if PLATFORM(MAC) 5023void HTMLMediaElement::updateDisableSleep() 5024{ 5025 if (!shouldDisableSleep() && m_sleepDisabler) 5026 m_sleepDisabler = nullptr; 5027 else if (shouldDisableSleep() && !m_sleepDisabler) 5028 m_sleepDisabler = DisplaySleepDisabler::create("com.apple.WebCore: HTMLMediaElement playback"); 5029} 5030 5031bool HTMLMediaElement::shouldDisableSleep() const 5032{ 5033 return m_player && !m_player->paused() && hasVideo() && hasAudio() && !loop(); 5034} 5035#endif 5036 5037String HTMLMediaElement::mediaPlayerReferrer() const 5038{ 5039 Frame* frame = document()->frame(); 5040 if (!frame) 5041 return String(); 5042 5043 return SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), m_currentSrc, frame->loader()->outgoingReferrer()); 5044} 5045 5046String HTMLMediaElement::mediaPlayerUserAgent() const 5047{ 5048 Frame* frame = document()->frame(); 5049 if (!frame) 5050 return String(); 5051 5052 return frame->loader()->userAgent(m_currentSrc); 5053 5054} 5055 5056MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const 5057{ 5058 if (!fastHasAttribute(HTMLNames::crossoriginAttr)) 5059 return Unspecified; 5060 if (equalIgnoringCase(fastGetAttribute(HTMLNames::crossoriginAttr), "use-credentials")) 5061 return UseCredentials; 5062 return Anonymous; 5063} 5064 5065bool HTMLMediaElement::mediaPlayerNeedsSiteSpecificHacks() const 5066{ 5067 Settings* settings = document()->settings(); 5068 return settings && settings->needsSiteSpecificQuirks(); 5069} 5070 5071String HTMLMediaElement::mediaPlayerDocumentHost() const 5072{ 5073 return document()->url().host(); 5074} 5075 5076void HTMLMediaElement::mediaPlayerEnterFullscreen() 5077{ 5078 enterFullscreen(); 5079} 5080 5081void HTMLMediaElement::mediaPlayerExitFullscreen() 5082{ 5083 exitFullscreen(); 5084} 5085 5086bool HTMLMediaElement::mediaPlayerIsFullscreen() const 5087{ 5088 return isFullscreen(); 5089} 5090 5091bool HTMLMediaElement::mediaPlayerIsFullscreenPermitted() const 5092{ 5093 return !userGestureRequiredForFullscreen() || ScriptController::processingUserGesture(); 5094} 5095 5096bool HTMLMediaElement::mediaPlayerIsVideo() const 5097{ 5098 return isVideo(); 5099} 5100 5101LayoutRect HTMLMediaElement::mediaPlayerContentBoxRect() const 5102{ 5103 if (renderer()) 5104 return renderer()->enclosingBox()->contentBoxRect(); 5105 return LayoutRect(); 5106} 5107 5108void HTMLMediaElement::mediaPlayerSetSize(const IntSize& size) 5109{ 5110 setAttribute(widthAttr, String::number(size.width())); 5111 setAttribute(heightAttr, String::number(size.height())); 5112} 5113 5114void HTMLMediaElement::mediaPlayerPause() 5115{ 5116 pause(); 5117} 5118 5119void HTMLMediaElement::mediaPlayerPlay() 5120{ 5121 play(); 5122} 5123 5124bool HTMLMediaElement::mediaPlayerIsPaused() const 5125{ 5126 return paused(); 5127} 5128 5129bool HTMLMediaElement::mediaPlayerIsLooping() const 5130{ 5131 return loop(); 5132} 5133 5134HostWindow* HTMLMediaElement::mediaPlayerHostWindow() 5135{ 5136 return mediaPlayerOwningDocument()->view()->hostWindow(); 5137} 5138 5139IntRect HTMLMediaElement::mediaPlayerWindowClipRect() 5140{ 5141 return mediaPlayerOwningDocument()->view()->windowClipRect(); 5142} 5143 5144CachedResourceLoader* HTMLMediaElement::mediaPlayerCachedResourceLoader() 5145{ 5146 return mediaPlayerOwningDocument()->cachedResourceLoader(); 5147} 5148 5149void HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture() 5150{ 5151 m_restrictions = NoRestrictions; 5152} 5153 5154bool HTMLMediaElement::doesHaveAttribute(const AtomicString& attribute) const 5155{ 5156 QualifiedName attributeName(nullAtom, attribute, nullAtom); 5157 if (!fastHasAttribute(attributeName)) 5158 return false; 5159 5160 if (Settings* settings = document()->settings()) { 5161 if (attributeName == HTMLNames::x_itunes_inherit_uri_query_componentAttr) 5162 return settings->enableInheritURIQueryComponent(); 5163 } 5164 5165 return true; 5166} 5167 5168bool MediaPlayer::doesHaveAttribute(const AtomicString& attribute) const 5169{ 5170 if (!m_mediaPlayerClient) 5171 return false; 5172 5173 return m_mediaPlayerClient->doesHaveAttribute(attribute); 5174} 5175 5176} 5177 5178#endif 5179