1 /* 2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 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 28 #if ENABLE(VIDEO) 29 #include "HTMLMediaElement.h" 30 31 #include "Attribute.h" 32 #include "Chrome.h" 33 #include "ChromeClient.h" 34 #include "ClientRect.h" 35 #include "ClientRectList.h" 36 #include "ContentSecurityPolicy.h" 37 #include "ContentType.h" 38 #include "CSSPropertyNames.h" 39 #include "CSSValueKeywords.h" 40 #include "Event.h" 41 #include "EventNames.h" 42 #include "ExceptionCode.h" 43 #include "Frame.h" 44 #include "FrameLoader.h" 45 #include "FrameLoaderClient.h" 46 #include "FrameView.h" 47 #include "HTMLDocument.h" 48 #include "HTMLNames.h" 49 #include "HTMLSourceElement.h" 50 #include "HTMLVideoElement.h" 51 #include "Logging.h" 52 #include "MediaControls.h" 53 #include "MediaDocument.h" 54 #include "MediaError.h" 55 #include "MediaList.h" 56 #include "MediaPlayer.h" 57 #include "MediaQueryEvaluator.h" 58 #include "MouseEvent.h" 59 #include "MIMETypeRegistry.h" 60 #include "Page.h" 61 #include "RenderVideo.h" 62 #include "RenderView.h" 63 #include "ScriptEventListener.h" 64 #include "Settings.h" 65 #include "ShadowRoot.h" 66 #include "TimeRanges.h" 67 #include <limits> 68 #include <wtf/CurrentTime.h> 69 #include <wtf/MathExtras.h> 70 #include <wtf/text/CString.h> 71 72 #if USE(ACCELERATED_COMPOSITING) 73 #include "RenderView.h" 74 #include "RenderLayerCompositor.h" 75 #endif 76 77 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 78 #include "RenderEmbeddedObject.h" 79 #include "Widget.h" 80 #endif 81 82 #if PLATFORM(ANDROID) 83 // For every touch, show the media control for 4 seconds. 84 #define TOUCH_DELAY 4 85 #endif 86 87 using namespace std; 88 89 namespace WebCore { 90 91 #if !LOG_DISABLED 92 static String urlForLogging(const String& url) 93 { 94 static const unsigned maximumURLLengthForLogging = 128; 95 96 if (url.length() < maximumURLLengthForLogging) 97 return url; 98 return url.substring(0, maximumURLLengthForLogging) + "..."; 99 } 100 101 static const char *boolString(bool val) 102 { 103 return val ? "true" : "false"; 104 } 105 #endif 106 107 #ifndef LOG_MEDIA_EVENTS 108 // Default to not logging events because so many are generated they can overwhelm the rest of 109 // the logging. 110 #define LOG_MEDIA_EVENTS 0 111 #endif 112 113 #ifndef LOG_CACHED_TIME_WARNINGS 114 // Default to not logging warnings about excessive drift in the cached media time because it adds a 115 // fair amount of overhead and logging. 116 #define LOG_CACHED_TIME_WARNINGS 0 117 #endif 118 119 static const float invalidMediaTime = -1; 120 121 using namespace HTMLNames; 122 123 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document) 124 : HTMLElement(tagName, document) 125 , ActiveDOMObject(document, this) 126 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) 127 , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired) 128 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) 129 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired) 130 , m_playedTimeRanges() 131 , m_playbackRate(1.0f) 132 , m_defaultPlaybackRate(1.0f) 133 , m_webkitPreservesPitch(true) 134 , m_networkState(NETWORK_EMPTY) 135 , m_readyState(HAVE_NOTHING) 136 , m_readyStateMaximum(HAVE_NOTHING) 137 , m_volume(1.0f) 138 , m_lastSeekTime(0) 139 , m_previousProgress(0) 140 , m_previousProgressTime(numeric_limits<double>::max()) 141 , m_lastTimeUpdateEventWallTime(0) 142 , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max()) 143 , m_loadState(WaitingForSource) 144 , m_currentSourceNode(0) 145 , m_nextChildNodeToConsider(0) 146 , m_player(0) 147 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 148 , m_proxyWidget(0) 149 #endif 150 , m_restrictions(RequireUserGestureForFullScreenRestriction) 151 , m_preload(MediaPlayer::Auto) 152 , m_displayMode(Unknown) 153 , m_processingMediaPlayerCallback(0) 154 , m_cachedTime(invalidMediaTime) 155 , m_cachedTimeWallClockUpdateTime(0) 156 , m_minimumWallClockTimeToCacheMediaTime(0) 157 , m_playing(false) 158 , m_isWaitingUntilMediaCanStart(false) 159 , m_shouldDelayLoadEvent(false) 160 , m_haveFiredLoadedData(false) 161 , m_inActiveDocument(true) 162 , m_autoplaying(true) 163 , m_muted(false) 164 , m_paused(true) 165 , m_seeking(false) 166 , m_sentStalledEvent(false) 167 , m_sentEndEvent(false) 168 , m_pausedInternal(false) 169 , m_sendProgressEvents(true) 170 , m_isFullscreen(false) 171 , m_closedCaptionsVisible(false) 172 , m_mouseOver(false) 173 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 174 , m_needWidgetUpdate(false) 175 #endif 176 , m_dispatchingCanPlayEvent(false) 177 , m_loadInitiatedByUserGesture(false) 178 , m_completelyLoaded(false) 179 #if PLATFORM(ANDROID) 180 , m_lastTouch(0) 181 #endif 182 { 183 LOG(Media, "HTMLMediaElement::HTMLMediaElement"); 184 document->registerForDocumentActivationCallbacks(this); 185 document->registerForMediaVolumeCallbacks(this); 186 document->registerForPrivateBrowsingStateChangedCallbacks(this); 187 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 188 // Enable the Media Element to listen to all the touch events 189 document->addListenerTypeIfNeeded(eventNames().touchstartEvent); 190 #endif 191 192 } 193 194 HTMLMediaElement::~HTMLMediaElement() 195 { 196 LOG(Media, "HTMLMediaElement::~HTMLMediaElement"); 197 if (m_isWaitingUntilMediaCanStart) 198 document()->removeMediaCanStartListener(this); 199 setShouldDelayLoadEvent(false); 200 document()->unregisterForDocumentActivationCallbacks(this); 201 document()->unregisterForMediaVolumeCallbacks(this); 202 document()->unregisterForPrivateBrowsingStateChangedCallbacks(this); 203 } 204 205 void HTMLMediaElement::willMoveToNewOwnerDocument() 206 { 207 if (m_isWaitingUntilMediaCanStart) 208 document()->removeMediaCanStartListener(this); 209 setShouldDelayLoadEvent(false); 210 document()->unregisterForDocumentActivationCallbacks(this); 211 document()->unregisterForMediaVolumeCallbacks(this); 212 HTMLElement::willMoveToNewOwnerDocument(); 213 } 214 215 void HTMLMediaElement::didMoveToNewOwnerDocument() 216 { 217 if (m_isWaitingUntilMediaCanStart) 218 document()->addMediaCanStartListener(this); 219 if (m_readyState < HAVE_CURRENT_DATA) 220 setShouldDelayLoadEvent(true); 221 document()->registerForDocumentActivationCallbacks(this); 222 document()->registerForMediaVolumeCallbacks(this); 223 HTMLElement::didMoveToNewOwnerDocument(); 224 } 225 226 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls) 227 { 228 HTMLElement::attributeChanged(attr, preserveDecls); 229 230 const QualifiedName& attrName = attr->name(); 231 if (attrName == srcAttr) { 232 // Trigger a reload, as long as the 'src' attribute is present. 233 if (!getAttribute(srcAttr).isEmpty()) 234 scheduleLoad(); 235 } 236 else if (attrName == controlsAttr) { 237 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 238 if (controls()) { 239 if (!hasMediaControls()) { 240 ensureMediaControls(); 241 mediaControls()->reset(); 242 } 243 mediaControls()->show(); 244 } else if (hasMediaControls()) 245 mediaControls()->hide(); 246 #else 247 if (m_player) 248 m_player->setControls(controls()); 249 #endif 250 } 251 } 252 253 void HTMLMediaElement::parseMappedAttribute(Attribute* attr) 254 { 255 const QualifiedName& attrName = attr->name(); 256 257 if (attrName == preloadAttr) { 258 String value = attr->value(); 259 260 if (equalIgnoringCase(value, "none")) 261 m_preload = MediaPlayer::None; 262 else if (equalIgnoringCase(value, "metadata")) 263 m_preload = MediaPlayer::MetaData; 264 else { 265 // The spec does not define an "invalid value default" but "auto" is suggested as the 266 // "missing value default", so use it for everything except "none" and "metadata" 267 m_preload = MediaPlayer::Auto; 268 } 269 270 // The attribute must be ignored if the autoplay attribute is present 271 if (!autoplay() && m_player) 272 m_player->setPreload(m_preload); 273 274 } else if (attrName == onabortAttr) 275 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr)); 276 else if (attrName == onbeforeloadAttr) 277 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); 278 else if (attrName == oncanplayAttr) 279 setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr)); 280 else if (attrName == oncanplaythroughAttr) 281 setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr)); 282 else if (attrName == ondurationchangeAttr) 283 setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr)); 284 else if (attrName == onemptiedAttr) 285 setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr)); 286 else if (attrName == onendedAttr) 287 setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr)); 288 else if (attrName == onerrorAttr) 289 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); 290 else if (attrName == onloadeddataAttr) 291 setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr)); 292 else if (attrName == onloadedmetadataAttr) 293 setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr)); 294 else if (attrName == onloadstartAttr) 295 setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr)); 296 else if (attrName == onpauseAttr) 297 setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr)); 298 else if (attrName == onplayAttr) 299 setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr)); 300 else if (attrName == onplayingAttr) 301 setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr)); 302 else if (attrName == onprogressAttr) 303 setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr)); 304 else if (attrName == onratechangeAttr) 305 setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr)); 306 else if (attrName == onseekedAttr) 307 setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr)); 308 else if (attrName == onseekingAttr) 309 setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr)); 310 else if (attrName == onstalledAttr) 311 setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr)); 312 else if (attrName == onsuspendAttr) 313 setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr)); 314 else if (attrName == ontimeupdateAttr) 315 setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr)); 316 else if (attrName == onvolumechangeAttr) 317 setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr)); 318 else if (attrName == onwaitingAttr) 319 setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr)); 320 else if (attrName == onwebkitbeginfullscreenAttr) 321 setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr)); 322 else if (attrName == onwebkitendfullscreenAttr) 323 setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr)); 324 else 325 HTMLElement::parseMappedAttribute(attr); 326 } 327 328 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style) 329 { 330 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 331 UNUSED_PARAM(style); 332 Frame* frame = document()->frame(); 333 if (!frame) 334 return false; 335 336 return true; 337 #else 338 return controls() ? HTMLElement::rendererIsNeeded(style) : false; 339 #endif 340 } 341 342 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*) 343 { 344 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 345 // Setup the renderer if we already have a proxy widget. 346 RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this); 347 if (m_proxyWidget) { 348 mediaRenderer->setWidget(m_proxyWidget); 349 350 Frame* frame = document()->frame(); 351 FrameLoader* loader = frame ? frame->loader() : 0; 352 if (loader) 353 loader->showMediaPlayerProxyPlugin(m_proxyWidget.get()); 354 } 355 return mediaRenderer; 356 #else 357 return new (arena) RenderMedia(this); 358 #endif 359 } 360 361 void HTMLMediaElement::insertedIntoDocument() 362 { 363 LOG(Media, "HTMLMediaElement::removedFromDocument"); 364 HTMLElement::insertedIntoDocument(); 365 if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY) 366 scheduleLoad(); 367 } 368 369 void HTMLMediaElement::removedFromDocument() 370 { 371 LOG(Media, "HTMLMediaElement::removedFromDocument"); 372 if (m_networkState > NETWORK_EMPTY) 373 pause(processingUserGesture()); 374 if (m_isFullscreen) 375 exitFullscreen(); 376 HTMLElement::removedFromDocument(); 377 } 378 379 void HTMLMediaElement::attach() 380 { 381 ASSERT(!attached()); 382 383 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 384 m_needWidgetUpdate = true; 385 #endif 386 387 HTMLElement::attach(); 388 389 if (renderer()) 390 renderer()->updateFromElement(); 391 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 392 else if (m_proxyWidget) { 393 Frame* frame = document()->frame(); 394 FrameLoader* loader = frame ? frame->loader() : 0; 395 if (loader) 396 loader->hideMediaPlayerProxyPlugin(m_proxyWidget.get()); 397 } 398 #endif 399 } 400 401 void HTMLMediaElement::recalcStyle(StyleChange change) 402 { 403 HTMLElement::recalcStyle(change); 404 405 if (renderer()) 406 renderer()->updateFromElement(); 407 } 408 409 void HTMLMediaElement::scheduleLoad() 410 { 411 LOG(Media, "HTMLMediaElement::scheduleLoad"); 412 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 413 createMediaPlayerProxy(); 414 #endif 415 416 if (m_loadTimer.isActive()) 417 return; 418 prepareForLoad(); 419 m_loadTimer.startOneShot(0); 420 } 421 422 void HTMLMediaElement::scheduleNextSourceChild() 423 { 424 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad. 425 m_loadTimer.startOneShot(0); 426 } 427 428 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) 429 { 430 #if LOG_MEDIA_EVENTS 431 LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data()); 432 #endif 433 m_pendingEvents.append(Event::create(eventName, false, true)); 434 if (!m_asyncEventTimer.isActive()) 435 m_asyncEventTimer.startOneShot(0); 436 } 437 438 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*) 439 { 440 Vector<RefPtr<Event> > pendingEvents; 441 ExceptionCode ec = 0; 442 443 m_pendingEvents.swap(pendingEvents); 444 unsigned count = pendingEvents.size(); 445 for (unsigned ndx = 0; ndx < count; ++ndx) { 446 #if LOG_MEDIA_EVENTS 447 LOG(Media, "HTMLMediaElement::asyncEventTimerFired - dispatching '%s'", pendingEvents[ndx]->type().string().ascii().data()); 448 #endif 449 if (pendingEvents[ndx]->type() == eventNames().canplayEvent) { 450 m_dispatchingCanPlayEvent = true; 451 dispatchEvent(pendingEvents[ndx].release(), ec); 452 m_dispatchingCanPlayEvent = false; 453 } else 454 dispatchEvent(pendingEvents[ndx].release(), ec); 455 } 456 } 457 458 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) 459 { 460 if (m_loadState == LoadingFromSourceElement) 461 loadNextSourceChild(); 462 else 463 loadInternal(); 464 } 465 466 PassRefPtr<MediaError> HTMLMediaElement::error() const 467 { 468 return m_error; 469 } 470 471 void HTMLMediaElement::setSrc(const String& url) 472 { 473 setAttribute(srcAttr, url); 474 } 475 476 String HTMLMediaElement::currentSrc() const 477 { 478 return m_currentSrc; 479 } 480 481 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const 482 { 483 return m_networkState; 484 } 485 486 String HTMLMediaElement::canPlayType(const String& mimeType) const 487 { 488 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType)); 489 String canPlay; 490 491 // 4.8.10.3 492 switch (support) 493 { 494 case MediaPlayer::IsNotSupported: 495 canPlay = ""; 496 break; 497 case MediaPlayer::MayBeSupported: 498 canPlay = "maybe"; 499 break; 500 case MediaPlayer::IsSupported: 501 canPlay = "probably"; 502 break; 503 } 504 505 LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data()); 506 507 return canPlay; 508 } 509 510 void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec) 511 { 512 LOG(Media, "HTMLMediaElement::load(isUserGesture : %s)", boolString(isUserGesture)); 513 514 if (m_restrictions & RequireUserGestureForLoadRestriction && !isUserGesture) 515 ec = INVALID_STATE_ERR; 516 else { 517 m_loadInitiatedByUserGesture = isUserGesture; 518 prepareForLoad(); 519 loadInternal(); 520 } 521 } 522 523 void HTMLMediaElement::prepareForLoad() 524 { 525 LOG(Media, "HTMLMediaElement::prepareForLoad"); 526 527 // Perform the cleanup required for the resource load algorithm to run. 528 stopPeriodicTimers(); 529 m_loadTimer.stop(); 530 m_sentStalledEvent = false; 531 m_haveFiredLoadedData = false; 532 m_completelyLoaded = false; 533 m_displayMode = Unknown; 534 535 // 1 - Abort any already-running instance of the resource selection algorithm for this element. 536 m_loadState = WaitingForSource; 537 m_currentSourceNode = 0; 538 539 // 2 - If there are any tasks from the media element's media element event task source in 540 // one of the task queues, then remove those tasks. 541 cancelPendingEventsAndCallbacks(); 542 543 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue 544 // a task to fire a simple event named abort at the media element. 545 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) 546 scheduleEvent(eventNames().abortEvent); 547 548 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 549 m_player = MediaPlayer::create(this); 550 #else 551 if (m_player) 552 m_player->cancelLoad(); 553 else 554 createMediaPlayerProxy(); 555 #endif 556 557 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps 558 if (m_networkState != NETWORK_EMPTY) { 559 m_networkState = NETWORK_EMPTY; 560 m_readyState = HAVE_NOTHING; 561 m_readyStateMaximum = HAVE_NOTHING; 562 refreshCachedTime(); 563 m_paused = true; 564 m_seeking = false; 565 invalidateCachedTime(); 566 scheduleEvent(eventNames().emptiedEvent); 567 } 568 569 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute. 570 setPlaybackRate(defaultPlaybackRate()); 571 572 // 6 - Set the error attribute to null and the autoplaying flag to true. 573 m_error = 0; 574 m_autoplaying = true; 575 576 // 7 - Invoke the media element's resource selection algorithm. 577 578 // 8 - Note: Playback of any previously playing media resource for this element stops. 579 580 // The resource selection algorithm 581 // 1 - Set the networkState to NETWORK_NO_SOURCE 582 m_networkState = NETWORK_NO_SOURCE; 583 584 // 2 - Asynchronously await a stable state. 585 586 m_playedTimeRanges = TimeRanges::create(); 587 m_lastSeekTime = 0; 588 m_closedCaptionsVisible = false; 589 590 // The spec doesn't say to block the load event until we actually run the asynchronous section 591 // algorithm, but do it now because we won't start that until after the timer fires and the 592 // event may have already fired by then. 593 setShouldDelayLoadEvent(true); 594 } 595 596 void HTMLMediaElement::loadInternal() 597 { 598 // If we can't start a load right away, start it later. 599 Page* page = document()->page(); 600 if (page && !page->canStartMedia()) { 601 if (m_isWaitingUntilMediaCanStart) 602 return; 603 document()->addMediaCanStartListener(this); 604 m_isWaitingUntilMediaCanStart = true; 605 return; 606 } 607 608 selectMediaResource(); 609 } 610 611 void HTMLMediaElement::selectMediaResource() 612 { 613 LOG(Media, "HTMLMediaElement::selectMediaResource"); 614 615 enum Mode { attribute, children }; 616 Mode mode = attribute; 617 618 // 3 - ... the media element has neither a src attribute ... 619 if (!hasAttribute(srcAttr)) { 620 // ... nor a source element child: ... 621 Node* node; 622 for (node = firstChild(); node; node = node->nextSibling()) { 623 if (node->hasTagName(sourceTag)) 624 break; 625 } 626 627 if (!node) { 628 m_loadState = WaitingForSource; 629 setShouldDelayLoadEvent(false); 630 631 // ... set the networkState to NETWORK_EMPTY, and abort these steps 632 m_networkState = NETWORK_EMPTY; 633 634 LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load"); 635 return; 636 } 637 638 mode = children; 639 } 640 641 // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event), 642 // and set its networkState to NETWORK_LOADING. 643 setShouldDelayLoadEvent(true); 644 m_networkState = NETWORK_LOADING; 645 646 // 5 647 scheduleEvent(eventNames().loadstartEvent); 648 649 // 6 - If mode is attribute, then run these substeps 650 if (mode == attribute) { 651 // If the src attribute's value is the empty string ... jump down to the failed step below 652 KURL mediaURL = getNonEmptyURLAttribute(srcAttr); 653 if (mediaURL.isEmpty()) { 654 noneSupported(); 655 LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'"); 656 return; 657 } 658 659 if (isSafeToLoadURL(mediaURL, Complain) && dispatchBeforeLoadEvent(mediaURL.string())) { 660 ContentType contentType(""); 661 m_loadState = LoadingFromSrcAttr; 662 loadResource(mediaURL, contentType); 663 } else 664 noneSupported(); 665 666 LOG(Media, "HTMLMediaElement::selectMediaResource, 'src' not used"); 667 return; 668 } 669 670 // Otherwise, the source elements will be used 671 m_currentSourceNode = 0; 672 loadNextSourceChild(); 673 } 674 675 void HTMLMediaElement::loadNextSourceChild() 676 { 677 ContentType contentType(""); 678 KURL mediaURL = selectNextSourceChild(&contentType, Complain); 679 if (!mediaURL.isValid()) { 680 waitForSourceChange(); 681 return; 682 } 683 684 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 685 // Recreate the media player for the new url 686 m_player = MediaPlayer::create(this); 687 #endif 688 689 m_loadState = LoadingFromSourceElement; 690 loadResource(mediaURL, contentType); 691 } 692 693 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType) 694 { 695 ASSERT(isSafeToLoadURL(initialURL, Complain)); 696 697 LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL.string()).utf8().data(), contentType.raw().utf8().data()); 698 699 Frame* frame = document()->frame(); 700 if (!frame) 701 return; 702 FrameLoader* loader = frame->loader(); 703 if (!loader) 704 return; 705 706 KURL url(initialURL); 707 if (!loader->willLoadMediaElementURL(url)) 708 return; 709 710 // The resource fetch algorithm 711 m_networkState = NETWORK_LOADING; 712 713 m_currentSrc = url; 714 715 LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data()); 716 717 if (m_sendProgressEvents) 718 startProgressEventTimer(); 719 720 Settings* settings = document()->settings(); 721 bool privateMode = !settings || settings->privateBrowsingEnabled(); 722 m_player->setPrivateBrowsingMode(privateMode); 723 724 if (!autoplay()) 725 m_player->setPreload(m_preload); 726 m_player->setPreservesPitch(m_webkitPreservesPitch); 727 updateVolume(); 728 729 #if PLATFORM(ANDROID) 730 if (isVideo()) 731 m_player->setMediaElementType(MediaPlayer::Video); 732 else 733 m_player->setMediaElementType(MediaPlayer::Audio); 734 #endif 735 m_player->load(m_currentSrc, contentType); 736 737 // If there is no poster to display, allow the media engine to render video frames as soon as 738 // they are available. 739 updateDisplayState(); 740 741 if (renderer()) 742 renderer()->updateFromElement(); 743 } 744 745 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid) 746 { 747 if (!url.isValid()) { 748 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url.string()).utf8().data()); 749 return false; 750 } 751 752 Frame* frame = document()->frame(); 753 if (!frame || !document()->securityOrigin()->canDisplay(url)) { 754 if (actionIfInvalid == Complain) 755 FrameLoader::reportLocalLoadFailed(frame, url.string()); 756 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url.string()).utf8().data()); 757 return false; 758 } 759 760 if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) 761 return false; 762 763 return true; 764 } 765 766 void HTMLMediaElement::startProgressEventTimer() 767 { 768 if (m_progressEventTimer.isActive()) 769 return; 770 771 m_previousProgressTime = WTF::currentTime(); 772 m_previousProgress = 0; 773 // 350ms is not magic, it is in the spec! 774 m_progressEventTimer.startRepeating(0.350); 775 } 776 777 void HTMLMediaElement::waitForSourceChange() 778 { 779 LOG(Media, "HTMLMediaElement::waitForSourceChange"); 780 781 stopPeriodicTimers(); 782 m_loadState = WaitingForSource; 783 784 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value 785 m_networkState = NETWORK_NO_SOURCE; 786 787 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 788 setShouldDelayLoadEvent(false); 789 } 790 791 void HTMLMediaElement::noneSupported() 792 { 793 LOG(Media, "HTMLMediaElement::noneSupported"); 794 795 stopPeriodicTimers(); 796 m_loadState = WaitingForSource; 797 m_currentSourceNode = 0; 798 799 // 5 - Reaching this step indicates that either the URL failed to resolve, or the media 800 // resource failed to load. Set the error attribute to a new MediaError object whose 801 // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED. 802 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); 803 804 // 6 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value. 805 m_networkState = NETWORK_NO_SOURCE; 806 807 // 7 - Queue a task to fire a progress event called error at the media element, in 808 // the context of the fetching process that was used to try to obtain the media 809 // resource in the resource fetch algorithm. 810 scheduleEvent(eventNames().errorEvent); 811 812 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 813 setShouldDelayLoadEvent(false); 814 815 // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource. 816 817 updateDisplayState(); 818 819 if (renderer()) 820 renderer()->updateFromElement(); 821 } 822 823 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) 824 { 825 LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code())); 826 827 // 1 - The user agent should cancel the fetching process. 828 stopPeriodicTimers(); 829 m_loadState = WaitingForSource; 830 831 // 2 - Set the error attribute to a new MediaError object whose code attribute is 832 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE. 833 m_error = err; 834 835 // 3 - Queue a task to fire a simple event named error at the media element. 836 scheduleEvent(eventNames().errorEvent); 837 838 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a 839 // task to fire a simple event called emptied at the element. 840 m_networkState = NETWORK_EMPTY; 841 scheduleEvent(eventNames().emptiedEvent); 842 843 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 844 setShouldDelayLoadEvent(false); 845 846 // 6 - Abort the overall resource selection algorithm. 847 m_currentSourceNode = 0; 848 } 849 850 void HTMLMediaElement::cancelPendingEventsAndCallbacks() 851 { 852 LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks"); 853 854 m_pendingEvents.clear(); 855 856 for (Node* node = firstChild(); node; node = node->nextSibling()) { 857 if (node->hasTagName(sourceTag)) 858 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent(); 859 } 860 } 861 862 Document* HTMLMediaElement::mediaPlayerOwningDocument() 863 { 864 Document* d = document(); 865 866 if (!d) 867 d = ownerDocument(); 868 869 return d; 870 } 871 872 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) 873 { 874 beginProcessingMediaPlayerCallback(); 875 setNetworkState(m_player->networkState()); 876 endProcessingMediaPlayerCallback(); 877 } 878 879 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) 880 { 881 LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState)); 882 883 if (state == MediaPlayer::Empty) { 884 // Just update the cached state and leave, we can't do anything. 885 m_networkState = NETWORK_EMPTY; 886 return; 887 } 888 889 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) { 890 stopPeriodicTimers(); 891 892 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more 893 // <source> children, schedule the next one 894 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) { 895 896 if (m_currentSourceNode) 897 m_currentSourceNode->scheduleErrorEvent(); 898 else 899 LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed"); 900 901 if (havePotentialSourceChild()) { 902 LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>"); 903 scheduleNextSourceChild(); 904 } else { 905 LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting"); 906 waitForSourceChange(); 907 } 908 909 return; 910 } 911 912 if (state == MediaPlayer::NetworkError) 913 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK)); 914 else if (state == MediaPlayer::DecodeError) 915 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE)); 916 else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr) 917 noneSupported(); 918 919 updateDisplayState(); 920 if (hasMediaControls()) 921 mediaControls()->reportedError(); 922 return; 923 } 924 925 if (state == MediaPlayer::Idle) { 926 if (m_networkState > NETWORK_IDLE) { 927 m_progressEventTimer.stop(); 928 scheduleEvent(eventNames().suspendEvent); 929 setShouldDelayLoadEvent(false); 930 } 931 m_networkState = NETWORK_IDLE; 932 } 933 934 if (state == MediaPlayer::Loading) { 935 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE) 936 startProgressEventTimer(); 937 m_networkState = NETWORK_LOADING; 938 } 939 940 if (state == MediaPlayer::Loaded) { 941 if (m_networkState != NETWORK_IDLE) { 942 m_progressEventTimer.stop(); 943 944 // Schedule one last progress event so we guarantee that at least one is fired 945 // for files that load very quickly. 946 scheduleEvent(eventNames().progressEvent); 947 } 948 m_networkState = NETWORK_IDLE; 949 m_completelyLoaded = true; 950 } 951 952 if (hasMediaControls()) 953 mediaControls()->changedNetworkState(); 954 } 955 956 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) 957 { 958 beginProcessingMediaPlayerCallback(); 959 960 setReadyState(m_player->readyState()); 961 962 endProcessingMediaPlayerCallback(); 963 } 964 965 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) 966 { 967 LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState)); 968 969 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it 970 bool wasPotentiallyPlaying = potentiallyPlaying(); 971 972 ReadyState oldState = m_readyState; 973 m_readyState = static_cast<ReadyState>(state); 974 975 if (m_readyState == oldState) 976 return; 977 978 if (oldState > m_readyStateMaximum) 979 m_readyStateMaximum = oldState; 980 981 if (m_networkState == NETWORK_EMPTY) 982 return; 983 984 if (m_seeking) { 985 // 4.8.10.9, step 11 986 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) 987 scheduleEvent(eventNames().waitingEvent); 988 989 // 4.8.10.10 step 14 & 15. 990 if (m_readyState >= HAVE_CURRENT_DATA) 991 finishSeek(); 992 } else { 993 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) { 994 // 4.8.10.8 995 scheduleTimeupdateEvent(false); 996 scheduleEvent(eventNames().waitingEvent); 997 } 998 } 999 1000 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) { 1001 scheduleEvent(eventNames().durationchangeEvent); 1002 scheduleEvent(eventNames().loadedmetadataEvent); 1003 if (hasMediaControls()) 1004 mediaControls()->loadedMetadata(); 1005 if (renderer()) 1006 renderer()->updateFromElement(); 1007 m_player->seek(0); 1008 } 1009 1010 bool shouldUpdateDisplayState = false; 1011 1012 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) { 1013 m_haveFiredLoadedData = true; 1014 shouldUpdateDisplayState = true; 1015 scheduleEvent(eventNames().loadeddataEvent); 1016 setShouldDelayLoadEvent(false); 1017 } 1018 1019 bool isPotentiallyPlaying = potentiallyPlaying(); 1020 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) { 1021 scheduleEvent(eventNames().canplayEvent); 1022 if (isPotentiallyPlaying) 1023 scheduleEvent(eventNames().playingEvent); 1024 shouldUpdateDisplayState = true; 1025 } 1026 1027 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) { 1028 if (oldState <= HAVE_CURRENT_DATA) 1029 scheduleEvent(eventNames().canplayEvent); 1030 1031 scheduleEvent(eventNames().canplaythroughEvent); 1032 1033 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) 1034 scheduleEvent(eventNames().playingEvent); 1035 1036 if (m_autoplaying && m_paused && autoplay()) { 1037 m_paused = false; 1038 invalidateCachedTime(); 1039 scheduleEvent(eventNames().playEvent); 1040 scheduleEvent(eventNames().playingEvent); 1041 } 1042 1043 shouldUpdateDisplayState = true; 1044 } 1045 1046 if (shouldUpdateDisplayState) 1047 updateDisplayState(); 1048 1049 updatePlayState(); 1050 } 1051 1052 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) 1053 { 1054 ASSERT(m_player); 1055 if (m_networkState != NETWORK_LOADING) 1056 return; 1057 1058 unsigned progress = m_player->bytesLoaded(); 1059 double time = WTF::currentTime(); 1060 double timedelta = time - m_previousProgressTime; 1061 1062 if (progress == m_previousProgress) { 1063 if (timedelta > 3.0 && !m_sentStalledEvent) { 1064 scheduleEvent(eventNames().stalledEvent); 1065 m_sentStalledEvent = true; 1066 setShouldDelayLoadEvent(false); 1067 } 1068 } else { 1069 scheduleEvent(eventNames().progressEvent); 1070 m_previousProgress = progress; 1071 m_previousProgressTime = time; 1072 m_sentStalledEvent = false; 1073 if (renderer()) 1074 renderer()->updateFromElement(); 1075 } 1076 } 1077 1078 void HTMLMediaElement::rewind(float timeDelta) 1079 { 1080 LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta); 1081 1082 ExceptionCode e; 1083 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e); 1084 } 1085 1086 void HTMLMediaElement::returnToRealtime() 1087 { 1088 LOG(Media, "HTMLMediaElement::returnToRealtime"); 1089 ExceptionCode e; 1090 setCurrentTime(maxTimeSeekable(), e); 1091 } 1092 1093 void HTMLMediaElement::addPlayedRange(float start, float end) 1094 { 1095 LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end); 1096 if (!m_playedTimeRanges) 1097 m_playedTimeRanges = TimeRanges::create(); 1098 m_playedTimeRanges->add(start, end); 1099 } 1100 1101 bool HTMLMediaElement::supportsSave() const 1102 { 1103 return m_player ? m_player->supportsSave() : false; 1104 } 1105 1106 void HTMLMediaElement::seek(float time, ExceptionCode& ec) 1107 { 1108 LOG(Media, "HTMLMediaElement::seek(%f)", time); 1109 1110 // 4.8.9.9 Seeking 1111 1112 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception. 1113 if (m_readyState == HAVE_NOTHING || !m_player) { 1114 ec = INVALID_STATE_ERR; 1115 return; 1116 } 1117 1118 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set. 1119 refreshCachedTime(); 1120 float now = currentTime(); 1121 1122 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is 1123 // already running. Abort that other instance of the algorithm without waiting for the step that 1124 // it is running to complete. 1125 // Nothing specific to be done here. 1126 1127 // 3 - Set the seeking IDL attribute to true. 1128 // The flag will be cleared when the engine tells us the time has actually changed. 1129 m_seeking = true; 1130 1131 // 5 - If the new playback position is later than the end of the media resource, then let it be the end 1132 // of the media resource instead. 1133 time = min(time, duration()); 1134 1135 // 6 - If the new playback position is less than the earliest possible position, let it be that position instead. 1136 float earliestTime = m_player->startTime(); 1137 time = max(time, earliestTime); 1138 1139 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This 1140 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's 1141 // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and 1142 // not generate a timechanged callback. This means m_seeking will never be cleared and we will never 1143 // fire a 'seeked' event. 1144 #if !LOG_DISABLED 1145 float mediaTime = m_player->mediaTimeForTimeValue(time); 1146 if (time != mediaTime) 1147 LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime); 1148 #endif 1149 time = m_player->mediaTimeForTimeValue(time); 1150 1151 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the 1152 // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute 1153 // that is the nearest to the new playback position. ... If there are no ranges given in the seekable 1154 // attribute then set the seeking IDL attribute to false and abort these steps. 1155 RefPtr<TimeRanges> seekableRanges = seekable(); 1156 1157 // Short circuit seeking to the current time by just firing the events if no seek is required. 1158 // Don't skip calling the media engine if we are in poster mode because a seek should always 1159 // cancel poster display. 1160 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster); 1161 if (noSeekRequired) { 1162 if (time == now) { 1163 scheduleEvent(eventNames().seekingEvent); 1164 scheduleTimeupdateEvent(false); 1165 scheduleEvent(eventNames().seekedEvent); 1166 } 1167 m_seeking = false; 1168 return; 1169 } 1170 time = seekableRanges->nearest(time); 1171 1172 if (m_playing) { 1173 if (m_lastSeekTime < now) 1174 addPlayedRange(m_lastSeekTime, now); 1175 } 1176 m_lastSeekTime = time; 1177 m_sentEndEvent = false; 1178 1179 // 8 - Set the current playback position to the given new playback position 1180 m_player->seek(time); 1181 1182 // 9 - Queue a task to fire a simple event named seeking at the element. 1183 scheduleEvent(eventNames().seekingEvent); 1184 1185 // 10 - Queue a task to fire a simple event named timeupdate at the element. 1186 scheduleTimeupdateEvent(false); 1187 1188 // 11-15 are handled, if necessary, when the engine signals a readystate change. 1189 } 1190 1191 void HTMLMediaElement::finishSeek() 1192 { 1193 LOG(Media, "HTMLMediaElement::finishSeek"); 1194 1195 // 4.8.10.9 Seeking step 14 1196 m_seeking = false; 1197 1198 // 4.8.10.9 Seeking step 15 1199 scheduleEvent(eventNames().seekedEvent); 1200 1201 setDisplayMode(Video); 1202 } 1203 1204 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const 1205 { 1206 return m_readyState; 1207 } 1208 1209 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const 1210 { 1211 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown; 1212 } 1213 1214 bool HTMLMediaElement::hasAudio() const 1215 { 1216 return m_player ? m_player->hasAudio() : false; 1217 } 1218 1219 bool HTMLMediaElement::seeking() const 1220 { 1221 return m_seeking; 1222 } 1223 1224 void HTMLMediaElement::refreshCachedTime() const 1225 { 1226 m_cachedTime = m_player->currentTime(); 1227 m_cachedTimeWallClockUpdateTime = WTF::currentTime(); 1228 } 1229 1230 void HTMLMediaElement::invalidateCachedTime() 1231 { 1232 LOG(Media, "HTMLMediaElement::invalidateCachedTime"); 1233 1234 // Don't try to cache movie time when playback first starts as the time reported by the engine 1235 // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it 1236 // too early. 1237 static const double minimumTimePlayingBeforeCacheSnapshot = 0.5; 1238 1239 m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot; 1240 m_cachedTime = invalidMediaTime; 1241 } 1242 1243 // playback state 1244 float HTMLMediaElement::currentTime() const 1245 { 1246 #if LOG_CACHED_TIME_WARNINGS 1247 static const double minCachedDeltaForWarning = 0.01; 1248 #endif 1249 1250 if (!m_player) 1251 return 0; 1252 1253 if (m_seeking) { 1254 LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime); 1255 return m_lastSeekTime; 1256 } 1257 1258 if (m_cachedTime != invalidMediaTime && m_paused) { 1259 #if LOG_CACHED_TIME_WARNINGS 1260 float delta = m_cachedTime - m_player->currentTime(); 1261 if (delta > minCachedDeltaForWarning) 1262 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta); 1263 #endif 1264 return m_cachedTime; 1265 } 1266 1267 // Is it too soon use a cached time? 1268 double now = WTF::currentTime(); 1269 double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime(); 1270 1271 if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) { 1272 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; 1273 1274 // Not too soon, use the cached time only if it hasn't expired. 1275 if (wallClockDelta < maximumDurationToCacheMediaTime) { 1276 float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta)); 1277 1278 #if LOG_CACHED_TIME_WARNINGS 1279 float delta = adjustedCacheTime - m_player->currentTime(); 1280 if (delta > minCachedDeltaForWarning) 1281 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta); 1282 #endif 1283 return adjustedCacheTime; 1284 } 1285 } 1286 1287 #if LOG_CACHED_TIME_WARNINGS 1288 if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) { 1289 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; 1290 float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime(); 1291 LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta); 1292 } 1293 #endif 1294 1295 refreshCachedTime(); 1296 1297 return m_cachedTime; 1298 } 1299 1300 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec) 1301 { 1302 seek(time, ec); 1303 } 1304 1305 float HTMLMediaElement::startTime() const 1306 { 1307 if (!m_player) 1308 return 0; 1309 return m_player->startTime(); 1310 } 1311 1312 float HTMLMediaElement::duration() const 1313 { 1314 if (m_player && m_readyState >= HAVE_METADATA) 1315 return m_player->duration(); 1316 1317 return numeric_limits<float>::quiet_NaN(); 1318 } 1319 1320 bool HTMLMediaElement::paused() const 1321 { 1322 return m_paused; 1323 } 1324 1325 float HTMLMediaElement::defaultPlaybackRate() const 1326 { 1327 return m_defaultPlaybackRate; 1328 } 1329 1330 void HTMLMediaElement::setDefaultPlaybackRate(float rate) 1331 { 1332 if (m_defaultPlaybackRate != rate) { 1333 m_defaultPlaybackRate = rate; 1334 scheduleEvent(eventNames().ratechangeEvent); 1335 } 1336 } 1337 1338 float HTMLMediaElement::playbackRate() const 1339 { 1340 return m_playbackRate; 1341 } 1342 1343 void HTMLMediaElement::setPlaybackRate(float rate) 1344 { 1345 LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate); 1346 1347 if (m_playbackRate != rate) { 1348 m_playbackRate = rate; 1349 invalidateCachedTime(); 1350 scheduleEvent(eventNames().ratechangeEvent); 1351 } 1352 if (m_player && potentiallyPlaying() && m_player->rate() != rate) 1353 m_player->setRate(rate); 1354 } 1355 1356 bool HTMLMediaElement::webkitPreservesPitch() const 1357 { 1358 return m_webkitPreservesPitch; 1359 } 1360 1361 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) 1362 { 1363 LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch)); 1364 1365 m_webkitPreservesPitch = preservesPitch; 1366 1367 if (!m_player) 1368 return; 1369 1370 m_player->setPreservesPitch(preservesPitch); 1371 } 1372 1373 bool HTMLMediaElement::ended() const 1374 { 1375 // 4.8.10.8 Playing the media resource 1376 // The ended attribute must return true if the media element has ended 1377 // playback and the direction of playback is forwards, and false otherwise. 1378 return endedPlayback() && m_playbackRate > 0; 1379 } 1380 1381 bool HTMLMediaElement::autoplay() const 1382 { 1383 return hasAttribute(autoplayAttr); 1384 } 1385 1386 void HTMLMediaElement::setAutoplay(bool b) 1387 { 1388 LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b)); 1389 setBooleanAttribute(autoplayAttr, b); 1390 } 1391 1392 String HTMLMediaElement::preload() const 1393 { 1394 switch (m_preload) { 1395 case MediaPlayer::None: 1396 return "none"; 1397 break; 1398 case MediaPlayer::MetaData: 1399 return "metadata"; 1400 break; 1401 case MediaPlayer::Auto: 1402 return "auto"; 1403 break; 1404 } 1405 1406 ASSERT_NOT_REACHED(); 1407 return String(); 1408 } 1409 1410 void HTMLMediaElement::setPreload(const String& preload) 1411 { 1412 LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data()); 1413 setAttribute(preloadAttr, preload); 1414 } 1415 1416 void HTMLMediaElement::play(bool isUserGesture) 1417 { 1418 LOG(Media, "HTMLMediaElement::play(isUserGesture : %s)", boolString(isUserGesture)); 1419 1420 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) 1421 return; 1422 1423 Document* doc = document(); 1424 Settings* settings = doc->settings(); 1425 if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) { 1426 // It should be impossible to be processing the canplay event while handling a user gesture 1427 // since it is dispatched asynchronously. 1428 ASSERT(!isUserGesture); 1429 String host = doc->baseURL().host(); 1430 if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org")) 1431 return; 1432 } 1433 1434 playInternal(); 1435 } 1436 1437 void HTMLMediaElement::playInternal() 1438 { 1439 LOG(Media, "HTMLMediaElement::playInternal"); 1440 1441 // 4.8.10.9. Playing the media resource 1442 if (!m_player || m_networkState == NETWORK_EMPTY) 1443 scheduleLoad(); 1444 1445 if (endedPlayback()) { 1446 ExceptionCode unused; 1447 seek(0, unused); 1448 } 1449 1450 if (m_paused) { 1451 m_paused = false; 1452 invalidateCachedTime(); 1453 scheduleEvent(eventNames().playEvent); 1454 1455 if (m_readyState <= HAVE_CURRENT_DATA) 1456 scheduleEvent(eventNames().waitingEvent); 1457 else if (m_readyState >= HAVE_FUTURE_DATA) 1458 scheduleEvent(eventNames().playingEvent); 1459 } 1460 m_autoplaying = false; 1461 1462 updatePlayState(); 1463 } 1464 1465 void HTMLMediaElement::pause(bool isUserGesture) 1466 { 1467 LOG(Media, "HTMLMediaElement::pause(isUserGesture : %s)", boolString(isUserGesture)); 1468 1469 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) 1470 return; 1471 1472 pauseInternal(); 1473 } 1474 1475 1476 void HTMLMediaElement::pauseInternal() 1477 { 1478 LOG(Media, "HTMLMediaElement::pauseInternal"); 1479 1480 // 4.8.10.9. Playing the media resource 1481 if (!m_player || m_networkState == NETWORK_EMPTY) 1482 scheduleLoad(); 1483 1484 m_autoplaying = false; 1485 1486 if (!m_paused) { 1487 m_paused = true; 1488 scheduleTimeupdateEvent(false); 1489 scheduleEvent(eventNames().pauseEvent); 1490 } 1491 1492 updatePlayState(); 1493 } 1494 1495 bool HTMLMediaElement::loop() const 1496 { 1497 return hasAttribute(loopAttr); 1498 } 1499 1500 void HTMLMediaElement::setLoop(bool b) 1501 { 1502 LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b)); 1503 setBooleanAttribute(loopAttr, b); 1504 } 1505 1506 bool HTMLMediaElement::controls() const 1507 { 1508 Frame* frame = document()->frame(); 1509 1510 // always show controls when scripting is disabled 1511 if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript)) 1512 return true; 1513 1514 // always show controls for video when fullscreen playback is required. 1515 if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback()) 1516 return true; 1517 1518 // Always show controls when in full screen mode. 1519 if (isFullscreen()) 1520 return true; 1521 1522 return hasAttribute(controlsAttr); 1523 } 1524 1525 void HTMLMediaElement::setControls(bool b) 1526 { 1527 LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b)); 1528 setBooleanAttribute(controlsAttr, b); 1529 } 1530 1531 float HTMLMediaElement::volume() const 1532 { 1533 return m_volume; 1534 } 1535 1536 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) 1537 { 1538 LOG(Media, "HTMLMediaElement::setVolume(%f)", vol); 1539 1540 if (vol < 0.0f || vol > 1.0f) { 1541 ec = INDEX_SIZE_ERR; 1542 return; 1543 } 1544 1545 if (m_volume != vol) { 1546 m_volume = vol; 1547 updateVolume(); 1548 scheduleEvent(eventNames().volumechangeEvent); 1549 } 1550 } 1551 1552 bool HTMLMediaElement::muted() const 1553 { 1554 return m_muted; 1555 } 1556 1557 void HTMLMediaElement::setMuted(bool muted) 1558 { 1559 LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted)); 1560 1561 if (m_muted != muted) { 1562 m_muted = muted; 1563 // Avoid recursion when the player reports volume changes. 1564 if (!processingMediaPlayerCallback()) { 1565 if (m_player) { 1566 m_player->setMuted(m_muted); 1567 if (hasMediaControls()) 1568 mediaControls()->changedMute(); 1569 } 1570 } 1571 scheduleEvent(eventNames().volumechangeEvent); 1572 } 1573 } 1574 1575 void HTMLMediaElement::togglePlayState() 1576 { 1577 LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay())); 1578 1579 // We can safely call the internal play/pause methods, which don't check restrictions, because 1580 // this method is only called from the built-in media controller 1581 if (canPlay()) { 1582 setPlaybackRate(defaultPlaybackRate()); 1583 playInternal(); 1584 } else 1585 pauseInternal(); 1586 } 1587 1588 void HTMLMediaElement::beginScrubbing() 1589 { 1590 LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused())); 1591 1592 if (!paused()) { 1593 if (ended()) { 1594 // Because a media element stays in non-paused state when it reaches end, playback resumes 1595 // when the slider is dragged from the end to another position unless we pause first. Do 1596 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. 1597 pause(processingUserGesture()); 1598 } else { 1599 // Not at the end but we still want to pause playback so the media engine doesn't try to 1600 // continue playing during scrubbing. Pause without generating an event as we will 1601 // unpause after scrubbing finishes. 1602 setPausedInternal(true); 1603 } 1604 } 1605 } 1606 1607 void HTMLMediaElement::endScrubbing() 1608 { 1609 LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal)); 1610 1611 if (m_pausedInternal) 1612 setPausedInternal(false); 1613 } 1614 1615 // The spec says to fire periodic timeupdate events (those sent while playing) every 1616 // "15 to 250ms", we choose the slowest frequency 1617 static const double maxTimeupdateEventFrequency = 0.25; 1618 1619 void HTMLMediaElement::startPlaybackProgressTimer() 1620 { 1621 if (m_playbackProgressTimer.isActive()) 1622 return; 1623 1624 m_previousProgressTime = WTF::currentTime(); 1625 m_previousProgress = 0; 1626 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); 1627 } 1628 1629 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) 1630 { 1631 ASSERT(m_player); 1632 if (!m_playbackRate) 1633 return; 1634 1635 scheduleTimeupdateEvent(true); 1636 if (hasMediaControls()) { 1637 #if PLATFORM(ANDROID) 1638 m_mouseOver = WTF::currentTime() - m_lastTouch <= TOUCH_DELAY; 1639 #endif 1640 if (!m_mouseOver && controls() && hasVideo()) 1641 mediaControls()->makeTransparent(); 1642 1643 mediaControls()->playbackProgressed(); 1644 } 1645 // FIXME: deal with cue ranges here 1646 } 1647 1648 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) 1649 { 1650 double now = WTF::currentTime(); 1651 double timedelta = now - m_lastTimeUpdateEventWallTime; 1652 1653 // throttle the periodic events 1654 if (periodicEvent && timedelta < maxTimeupdateEventFrequency) 1655 return; 1656 1657 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one 1658 // event at a given time so filter here 1659 float movieTime = currentTime(); 1660 if (movieTime != m_lastTimeUpdateEventMovieTime) { 1661 scheduleEvent(eventNames().timeupdateEvent); 1662 m_lastTimeUpdateEventWallTime = now; 1663 m_lastTimeUpdateEventMovieTime = movieTime; 1664 } 1665 } 1666 1667 bool HTMLMediaElement::canPlay() const 1668 { 1669 return paused() || ended() || m_readyState < HAVE_METADATA; 1670 } 1671 1672 float HTMLMediaElement::percentLoaded() const 1673 { 1674 if (!m_player) 1675 return 0; 1676 float duration = m_player->duration(); 1677 1678 if (!duration || isinf(duration)) 1679 return 0; 1680 1681 float buffered = 0; 1682 RefPtr<TimeRanges> timeRanges = m_player->buffered(); 1683 for (unsigned i = 0; i < timeRanges->length(); ++i) { 1684 ExceptionCode ignoredException; 1685 float start = timeRanges->start(i, ignoredException); 1686 float end = timeRanges->end(i, ignoredException); 1687 buffered += end - start; 1688 } 1689 return buffered / duration; 1690 } 1691 1692 bool HTMLMediaElement::havePotentialSourceChild() 1693 { 1694 // Stash the current <source> node and next nodes so we can restore them after checking 1695 // to see there is another potential. 1696 HTMLSourceElement* currentSourceNode = m_currentSourceNode; 1697 Node* nextNode = m_nextChildNodeToConsider; 1698 1699 KURL nextURL = selectNextSourceChild(0, DoNothing); 1700 1701 m_currentSourceNode = currentSourceNode; 1702 m_nextChildNodeToConsider = nextNode; 1703 1704 return nextURL.isValid(); 1705 } 1706 1707 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid) 1708 { 1709 #if !LOG_DISABLED 1710 // Don't log if this was just called to find out if there are any valid <source> elements. 1711 bool shouldLog = actionIfInvalid != DoNothing; 1712 if (shouldLog) 1713 LOG(Media, "HTMLMediaElement::selectNextSourceChild(contentType : \"%s\")", contentType ? contentType->raw().utf8().data() : ""); 1714 #endif 1715 1716 if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) { 1717 #if !LOG_DISABLED 1718 if (shouldLog) 1719 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\""); 1720 #endif 1721 return KURL(); 1722 } 1723 1724 KURL mediaURL; 1725 Node* node; 1726 HTMLSourceElement* source = 0; 1727 bool lookingForStartNode = m_nextChildNodeToConsider; 1728 bool canUse = false; 1729 1730 for (node = firstChild(); !canUse && node; node = node->nextSibling()) { 1731 if (lookingForStartNode && m_nextChildNodeToConsider != node) 1732 continue; 1733 lookingForStartNode = false; 1734 1735 if (!node->hasTagName(sourceTag)) 1736 continue; 1737 1738 source = static_cast<HTMLSourceElement*>(node); 1739 1740 // 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 1741 mediaURL = source->getNonEmptyURLAttribute(srcAttr); 1742 #if !LOG_DISABLED 1743 if (shouldLog) 1744 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data()); 1745 #endif 1746 if (mediaURL.isEmpty()) 1747 goto check_again; 1748 1749 if (source->hasAttribute(mediaAttr)) { 1750 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); 1751 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); 1752 #if !LOG_DISABLED 1753 if (shouldLog) 1754 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data()); 1755 #endif 1756 if (!screenEval.eval(media.get())) 1757 goto check_again; 1758 } 1759 1760 if (source->hasAttribute(typeAttr)) { 1761 #if !LOG_DISABLED 1762 if (shouldLog) 1763 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data()); 1764 #endif 1765 if (!MediaPlayer::supportsType(ContentType(source->type()))) 1766 goto check_again; 1767 } 1768 1769 // Is it safe to load this url? 1770 if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string())) 1771 goto check_again; 1772 1773 // Making it this far means the <source> looks reasonable. 1774 canUse = true; 1775 1776 check_again: 1777 if (!canUse && actionIfInvalid == Complain) 1778 source->scheduleErrorEvent(); 1779 } 1780 1781 if (canUse) { 1782 if (contentType) 1783 *contentType = ContentType(source->type()); 1784 m_currentSourceNode = source; 1785 m_nextChildNodeToConsider = source->nextSibling(); 1786 if (!m_nextChildNodeToConsider) 1787 m_nextChildNodeToConsider = sourceChildEndOfListValue(); 1788 } else { 1789 m_currentSourceNode = 0; 1790 m_nextChildNodeToConsider = sourceChildEndOfListValue(); 1791 } 1792 1793 #if !LOG_DISABLED 1794 if (shouldLog) 1795 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL.string()).utf8().data() : ""); 1796 #endif 1797 return canUse ? mediaURL : KURL(); 1798 } 1799 1800 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source) 1801 { 1802 LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source); 1803 1804 #if !LOG_DISABLED 1805 if (source->hasTagName(sourceTag)) { 1806 KURL url = source->getNonEmptyURLAttribute(srcAttr); 1807 LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data()); 1808 } 1809 #endif 1810 1811 // We should only consider a <source> element when there is not src attribute at all. 1812 if (hasAttribute(srcAttr)) 1813 return; 1814 1815 // 4.8.8 - If a source element is inserted as a child of a media element that has no src 1816 // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke 1817 // the media element's resource selection algorithm. 1818 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) { 1819 scheduleLoad(); 1820 return; 1821 } 1822 1823 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) { 1824 LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source"); 1825 m_nextChildNodeToConsider = source; 1826 return; 1827 } 1828 1829 if (m_nextChildNodeToConsider != sourceChildEndOfListValue()) 1830 return; 1831 1832 // 4.8.9.5, resource selection algorithm, source elements section: 1833 // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.) 1834 // 21 - Asynchronously await a stable state... 1835 // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case 1836 // it hasn't been fired yet). 1837 setShouldDelayLoadEvent(true); 1838 1839 // 23 - Set the networkState back to NETWORK_LOADING. 1840 m_networkState = NETWORK_LOADING; 1841 1842 // 24 - Jump back to the find next candidate step above. 1843 m_nextChildNodeToConsider = source; 1844 scheduleNextSourceChild(); 1845 } 1846 1847 void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source) 1848 { 1849 LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source); 1850 1851 #if !LOG_DISABLED 1852 if (source->hasTagName(sourceTag)) { 1853 KURL url = source->getNonEmptyURLAttribute(srcAttr); 1854 LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data()); 1855 } 1856 #endif 1857 1858 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider) 1859 return; 1860 1861 if (source == m_nextChildNodeToConsider) { 1862 m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling(); 1863 if (!m_nextChildNodeToConsider) 1864 m_nextChildNodeToConsider = sourceChildEndOfListValue(); 1865 LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider); 1866 } else if (source == m_currentSourceNode) { 1867 // Clear the current source node pointer, but don't change the movie as the spec says: 1868 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already 1869 // inserted in a video or audio element will have no effect. 1870 m_currentSourceNode = 0; 1871 LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0"); 1872 } 1873 } 1874 1875 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) 1876 { 1877 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged"); 1878 1879 beginProcessingMediaPlayerCallback(); 1880 1881 invalidateCachedTime(); 1882 1883 // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek. 1884 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA) 1885 finishSeek(); 1886 1887 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, 1888 // it will only queue a 'timeupdate' event if we haven't already posted one at the current 1889 // movie time. 1890 scheduleTimeupdateEvent(false); 1891 1892 float now = currentTime(); 1893 float dur = duration(); 1894 if (!isnan(dur) && dur && now >= dur) { 1895 if (loop()) { 1896 ExceptionCode ignoredException; 1897 m_sentEndEvent = false; 1898 seek(0, ignoredException); 1899 } else { 1900 if (!m_sentEndEvent) { 1901 m_sentEndEvent = true; 1902 scheduleEvent(eventNames().endedEvent); 1903 } 1904 } 1905 } 1906 else 1907 m_sentEndEvent = false; 1908 1909 updatePlayState(); 1910 endProcessingMediaPlayerCallback(); 1911 } 1912 1913 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) 1914 { 1915 LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged"); 1916 1917 beginProcessingMediaPlayerCallback(); 1918 if (m_player) { 1919 float vol = m_player->volume(); 1920 if (vol != m_volume) { 1921 m_volume = vol; 1922 updateVolume(); 1923 scheduleEvent(eventNames().volumechangeEvent); 1924 } 1925 } 1926 endProcessingMediaPlayerCallback(); 1927 } 1928 1929 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) 1930 { 1931 LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged"); 1932 1933 beginProcessingMediaPlayerCallback(); 1934 if (m_player) 1935 setMuted(m_player->muted()); 1936 endProcessingMediaPlayerCallback(); 1937 } 1938 1939 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) 1940 { 1941 LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged"); 1942 1943 beginProcessingMediaPlayerCallback(); 1944 scheduleEvent(eventNames().durationchangeEvent); 1945 if (renderer()) 1946 renderer()->updateFromElement(); 1947 endProcessingMediaPlayerCallback(); 1948 1949 #if PLATFORM(ANDROID) 1950 if (hasMediaControls()) 1951 mediaControls()->reset(); 1952 #endif 1953 } 1954 1955 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) 1956 { 1957 LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged"); 1958 1959 beginProcessingMediaPlayerCallback(); 1960 1961 invalidateCachedTime(); 1962 1963 // Stash the rate in case the one we tried to set isn't what the engine is 1964 // using (eg. it can't handle the rate we set) 1965 m_playbackRate = m_player->rate(); 1966 invalidateCachedTime(); 1967 endProcessingMediaPlayerCallback(); 1968 } 1969 1970 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*) 1971 { 1972 LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged"); 1973 1974 if (!m_player || m_pausedInternal) 1975 return; 1976 1977 beginProcessingMediaPlayerCallback(); 1978 if (m_player->paused()) 1979 pauseInternal(); 1980 else 1981 playInternal(); 1982 endProcessingMediaPlayerCallback(); 1983 } 1984 1985 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) 1986 { 1987 LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks"); 1988 1989 // The MediaPlayer came across content it cannot completely handle. 1990 // This is normally acceptable except when we are in a standalone 1991 // MediaDocument. If so, tell the document what has happened. 1992 if (ownerDocument()->isMediaDocument()) { 1993 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument()); 1994 mediaDocument->mediaElementSawUnsupportedTracks(); 1995 } 1996 } 1997 1998 // MediaPlayerPresentation methods 1999 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) 2000 { 2001 beginProcessingMediaPlayerCallback(); 2002 updateDisplayState(); 2003 if (renderer()) 2004 renderer()->repaint(); 2005 endProcessingMediaPlayerCallback(); 2006 } 2007 2008 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) 2009 { 2010 LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged"); 2011 2012 beginProcessingMediaPlayerCallback(); 2013 if (renderer()) 2014 renderer()->updateFromElement(); 2015 endProcessingMediaPlayerCallback(); 2016 } 2017 2018 #if USE(ACCELERATED_COMPOSITING) 2019 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) 2020 { 2021 if (renderer() && renderer()->isVideo()) { 2022 ASSERT(renderer()->view()); 2023 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer())); 2024 } 2025 return false; 2026 } 2027 2028 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*) 2029 { 2030 LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged"); 2031 2032 // Kick off a fake recalcStyle that will update the compositing tree. 2033 setNeedsStyleRecalc(SyntheticStyleChange); 2034 } 2035 #endif 2036 2037 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*) 2038 { 2039 LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated"); 2040 beginProcessingMediaPlayerCallback(); 2041 if (renderer()) 2042 renderer()->updateFromElement(); 2043 endProcessingMediaPlayerCallback(); 2044 } 2045 2046 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*) 2047 { 2048 LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable"); 2049 beginProcessingMediaPlayerCallback(); 2050 if (displayMode() == PosterWaitingForVideo) { 2051 setDisplayMode(Video); 2052 #if USE(ACCELERATED_COMPOSITING) 2053 mediaPlayerRenderingModeChanged(m_player.get()); 2054 #endif 2055 } 2056 endProcessingMediaPlayerCallback(); 2057 } 2058 2059 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const 2060 { 2061 if (!m_player) 2062 return TimeRanges::create(); 2063 return m_player->buffered(); 2064 } 2065 2066 PassRefPtr<TimeRanges> HTMLMediaElement::played() 2067 { 2068 if (m_playing) { 2069 float time = currentTime(); 2070 if (time > m_lastSeekTime) 2071 addPlayedRange(m_lastSeekTime, time); 2072 } 2073 2074 if (!m_playedTimeRanges) 2075 m_playedTimeRanges = TimeRanges::create(); 2076 2077 return m_playedTimeRanges->copy(); 2078 } 2079 2080 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const 2081 { 2082 // FIXME real ranges support 2083 if (!maxTimeSeekable()) 2084 return TimeRanges::create(); 2085 return TimeRanges::create(minTimeSeekable(), maxTimeSeekable()); 2086 } 2087 2088 bool HTMLMediaElement::potentiallyPlaying() const 2089 { 2090 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing 2091 // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the 2092 // checks in couldPlayIfEnoughData(). 2093 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA; 2094 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData(); 2095 } 2096 2097 bool HTMLMediaElement::couldPlayIfEnoughData() const 2098 { 2099 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); 2100 } 2101 2102 bool HTMLMediaElement::endedPlayback() const 2103 { 2104 float dur = duration(); 2105 if (!m_player || isnan(dur)) 2106 return false; 2107 2108 // 4.8.10.8 Playing the media resource 2109 2110 // A media element is said to have ended playback when the element's 2111 // readyState attribute is HAVE_METADATA or greater, 2112 if (m_readyState < HAVE_METADATA) 2113 return false; 2114 2115 // and the current playback position is the end of the media resource and the direction 2116 // of playback is forwards and the media element does not have a loop attribute specified, 2117 float now = currentTime(); 2118 if (m_playbackRate > 0) 2119 return dur > 0 && now >= dur && !loop(); 2120 2121 // or the current playback position is the earliest possible position and the direction 2122 // of playback is backwards 2123 if (m_playbackRate < 0) 2124 return now <= 0; 2125 2126 return false; 2127 } 2128 2129 bool HTMLMediaElement::stoppedDueToErrors() const 2130 { 2131 if (m_readyState >= HAVE_METADATA && m_error) { 2132 RefPtr<TimeRanges> seekableRanges = seekable(); 2133 if (!seekableRanges->contain(currentTime())) 2134 return true; 2135 } 2136 2137 return false; 2138 } 2139 2140 bool HTMLMediaElement::pausedForUserInteraction() const 2141 { 2142 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] 2143 return false; 2144 } 2145 2146 float HTMLMediaElement::minTimeSeekable() const 2147 { 2148 return 0; 2149 } 2150 2151 float HTMLMediaElement::maxTimeSeekable() const 2152 { 2153 return m_player ? m_player->maxTimeSeekable() : 0; 2154 } 2155 2156 void HTMLMediaElement::updateVolume() 2157 { 2158 if (!m_player) 2159 return; 2160 2161 // Avoid recursion when the player reports volume changes. 2162 if (!processingMediaPlayerCallback()) { 2163 Page* page = document()->page(); 2164 float volumeMultiplier = page ? page->mediaVolume() : 1; 2165 2166 m_player->setMuted(m_muted); 2167 m_player->setVolume(m_volume * volumeMultiplier); 2168 } 2169 2170 if (hasMediaControls()) 2171 mediaControls()->changedVolume(); 2172 } 2173 2174 void HTMLMediaElement::updatePlayState() 2175 { 2176 if (!m_player) 2177 return; 2178 2179 if (m_pausedInternal) { 2180 if (!m_player->paused()) 2181 m_player->pause(); 2182 refreshCachedTime(); 2183 m_playbackProgressTimer.stop(); 2184 if (hasMediaControls()) 2185 mediaControls()->playbackStopped(); 2186 return; 2187 } 2188 2189 bool shouldBePlaying = potentiallyPlaying(); 2190 bool playerPaused = m_player->paused(); 2191 2192 LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s", 2193 boolString(shouldBePlaying), boolString(playerPaused)); 2194 2195 if (shouldBePlaying) { 2196 setDisplayMode(Video); 2197 invalidateCachedTime(); 2198 2199 if (playerPaused) { 2200 if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback()) 2201 enterFullscreen(); 2202 2203 // Set rate, muted before calling play in case they were set before the media engine was setup. 2204 // The media engine should just stash the rate and muted values since it isn't already playing. 2205 m_player->setRate(m_playbackRate); 2206 m_player->setMuted(m_muted); 2207 2208 m_player->play(); 2209 } 2210 2211 if (hasMediaControls()) 2212 mediaControls()->playbackStarted(); 2213 startPlaybackProgressTimer(); 2214 m_playing = true; 2215 2216 } else { // Should not be playing right now 2217 if (!playerPaused) 2218 m_player->pause(); 2219 refreshCachedTime(); 2220 2221 m_playbackProgressTimer.stop(); 2222 m_playing = false; 2223 float time = currentTime(); 2224 if (time > m_lastSeekTime) 2225 addPlayedRange(m_lastSeekTime, time); 2226 2227 if (couldPlayIfEnoughData()) 2228 m_player->prepareToPlay(); 2229 2230 if (hasMediaControls()) 2231 mediaControls()->playbackStopped(); 2232 } 2233 2234 if (renderer()) 2235 renderer()->updateFromElement(); 2236 } 2237 2238 void HTMLMediaElement::setPausedInternal(bool b) 2239 { 2240 m_pausedInternal = b; 2241 updatePlayState(); 2242 } 2243 2244 void HTMLMediaElement::stopPeriodicTimers() 2245 { 2246 m_progressEventTimer.stop(); 2247 m_playbackProgressTimer.stop(); 2248 } 2249 2250 void HTMLMediaElement::userCancelledLoad() 2251 { 2252 LOG(Media, "HTMLMediaElement::userCancelledLoad"); 2253 2254 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded) 2255 return; 2256 2257 // If the media data fetching process is aborted by the user: 2258 2259 // 1 - The user agent should cancel the fetching process. 2260 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2261 m_player.clear(); 2262 #endif 2263 stopPeriodicTimers(); 2264 m_loadTimer.stop(); 2265 m_loadState = WaitingForSource; 2266 2267 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED. 2268 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); 2269 2270 // 3 - Queue a task to fire a simple event named error at the media element. 2271 scheduleEvent(eventNames().abortEvent); 2272 2273 // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the 2274 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a 2275 // simple event named emptied at the element. Otherwise, set the element's networkState 2276 // attribute to the NETWORK_IDLE value. 2277 if (m_readyState == HAVE_NOTHING) { 2278 m_networkState = NETWORK_EMPTY; 2279 scheduleEvent(eventNames().emptiedEvent); 2280 } 2281 else 2282 m_networkState = NETWORK_IDLE; 2283 2284 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 2285 setShouldDelayLoadEvent(false); 2286 2287 // 6 - Abort the overall resource selection algorithm. 2288 m_currentSourceNode = 0; 2289 2290 // Reset m_readyState since m_player is gone. 2291 m_readyState = HAVE_NOTHING; 2292 } 2293 2294 bool HTMLMediaElement::canSuspend() const 2295 { 2296 return true; 2297 } 2298 2299 void HTMLMediaElement::stop() 2300 { 2301 LOG(Media, "HTMLMediaElement::stop"); 2302 if (m_isFullscreen) 2303 exitFullscreen(); 2304 2305 m_inActiveDocument = false; 2306 userCancelledLoad(); 2307 2308 // Stop the playback without generating events 2309 setPausedInternal(true); 2310 2311 if (renderer()) 2312 renderer()->updateFromElement(); 2313 2314 stopPeriodicTimers(); 2315 cancelPendingEventsAndCallbacks(); 2316 } 2317 2318 void HTMLMediaElement::suspend(ReasonForSuspension why) 2319 { 2320 LOG(Media, "HTMLMediaElement::suspend"); 2321 2322 switch (why) 2323 { 2324 case DocumentWillBecomeInactive: 2325 stop(); 2326 break; 2327 case JavaScriptDebuggerPaused: 2328 case WillShowDialog: 2329 // Do nothing, we don't pause media playback in these cases. 2330 break; 2331 } 2332 } 2333 2334 void HTMLMediaElement::resume() 2335 { 2336 LOG(Media, "HTMLMediaElement::resume"); 2337 2338 m_inActiveDocument = true; 2339 setPausedInternal(false); 2340 2341 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { 2342 // Restart the load if it was aborted in the middle by moving the document to the page cache. 2343 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to 2344 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). 2345 // This behavior is not specified but it seems like a sensible thing to do. 2346 ExceptionCode ec; 2347 load(processingUserGesture(), ec); 2348 } 2349 2350 if (renderer()) 2351 renderer()->updateFromElement(); 2352 } 2353 2354 bool HTMLMediaElement::hasPendingActivity() const 2355 { 2356 // Return true when we have pending events so we can't fire events after the JS 2357 // object gets collected. 2358 bool pending = m_pendingEvents.size(); 2359 LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending)); 2360 return pending; 2361 } 2362 2363 void HTMLMediaElement::mediaVolumeDidChange() 2364 { 2365 LOG(Media, "HTMLMediaElement::mediaVolumeDidChange"); 2366 updateVolume(); 2367 } 2368 2369 void HTMLMediaElement::defaultEventHandler(Event* event) 2370 { 2371 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2372 RenderObject* r = renderer(); 2373 if (!r || !r->isWidget()) 2374 return; 2375 2376 Widget* widget = toRenderWidget(r)->widget(); 2377 if (widget) 2378 widget->handleEvent(event); 2379 #else 2380 if (event->isMouseEvent()) { 2381 #if PLATFORM(ANDROID) 2382 m_lastTouch = WTF::currentTime(); 2383 #endif 2384 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); 2385 if (mouseEvent->relatedTarget() != this) { 2386 if (event->type() == eventNames().mouseoverEvent) { 2387 m_mouseOver = true; 2388 if (hasMediaControls() && controls() && !canPlay()) 2389 mediaControls()->makeOpaque(); 2390 } else if (event->type() == eventNames().mouseoutEvent) 2391 m_mouseOver = false; 2392 } 2393 } 2394 2395 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 2396 if (event->isTouchEvent()) { 2397 m_mouseOver = !(event->type() == eventNames().touchendEvent || event->type() == eventNames().touchcancelEvent); 2398 if (m_mouseOver && hasMediaControls() && controls() && !canPlay()) { 2399 m_lastTouch = WTF::currentTime(); 2400 mediaControls()->makeOpaque(); 2401 } 2402 } 2403 #endif 2404 2405 HTMLElement::defaultEventHandler(event); 2406 #endif 2407 } 2408 2409 bool HTMLMediaElement::processingUserGesture() const 2410 { 2411 Frame* frame = document()->frame(); 2412 FrameLoader* loader = frame ? frame->loader() : 0; 2413 2414 // return 'true' for safety if we don't know the answer 2415 return loader ? loader->isProcessingUserGesture() : true; 2416 } 2417 2418 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2419 2420 void HTMLMediaElement::ensureMediaPlayer() 2421 { 2422 if (!m_player) 2423 m_player = MediaPlayer::create(this); 2424 } 2425 2426 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) 2427 { 2428 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { 2429 togglePlayState(); 2430 return; 2431 } 2432 2433 if (m_player) 2434 m_player->deliverNotification(notification); 2435 } 2436 2437 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) 2438 { 2439 ensureMediaPlayer(); 2440 m_player->setMediaPlayerProxy(proxy); 2441 } 2442 2443 void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values) 2444 { 2445 Frame* frame = document()->frame(); 2446 FrameLoader* loader = frame ? frame->loader() : 0; 2447 2448 if (isVideo()) { 2449 KURL posterURL = getNonEmptyURLAttribute(posterAttr); 2450 if (!posterURL.isEmpty() && loader && loader->willLoadMediaElementURL(posterURL)) { 2451 names.append("_media_element_poster_"); 2452 values.append(posterURL.string()); 2453 } 2454 } 2455 2456 if (controls()) { 2457 names.append("_media_element_controls_"); 2458 values.append("true"); 2459 } 2460 2461 url = src(); 2462 if (!isSafeToLoadURL(url, Complain)) 2463 url = selectNextSourceChild(0, DoNothing); 2464 2465 m_currentSrc = url.string(); 2466 if (url.isValid() && loader && loader->willLoadMediaElementURL(url)) { 2467 names.append("_media_element_src_"); 2468 values.append(m_currentSrc); 2469 } 2470 } 2471 2472 void HTMLMediaElement::finishParsingChildren() 2473 { 2474 HTMLElement::finishParsingChildren(); 2475 document()->updateStyleIfNeeded(); 2476 createMediaPlayerProxy(); 2477 } 2478 2479 void HTMLMediaElement::createMediaPlayerProxy() 2480 { 2481 ensureMediaPlayer(); 2482 2483 if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate)) 2484 return; 2485 2486 Frame* frame = document()->frame(); 2487 FrameLoader* loader = frame ? frame->loader() : 0; 2488 if (!loader) 2489 return; 2490 2491 LOG(Media, "HTMLMediaElement::createMediaPlayerProxy"); 2492 2493 KURL url; 2494 Vector<String> paramNames; 2495 Vector<String> paramValues; 2496 2497 getPluginProxyParams(url, paramNames, paramValues); 2498 2499 // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to 2500 // display:none 2501 m_proxyWidget = loader->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues); 2502 if (m_proxyWidget) 2503 m_needWidgetUpdate = false; 2504 } 2505 2506 void HTMLMediaElement::updateWidget(PluginCreationOption) 2507 { 2508 mediaElement->setNeedWidgetUpdate(false); 2509 2510 Vector<String> paramNames; 2511 Vector<String> paramValues; 2512 KURL kurl; 2513 2514 mediaElement->getPluginProxyParams(kurl, paramNames, paramValues); 2515 SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); 2516 loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues); 2517 } 2518 2519 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2520 2521 bool HTMLMediaElement::isFullscreen() const 2522 { 2523 if (m_isFullscreen) 2524 return true; 2525 2526 #if ENABLE(FULLSCREEN_API) 2527 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) 2528 return true; 2529 #endif 2530 2531 return false; 2532 } 2533 2534 void HTMLMediaElement::enterFullscreen() 2535 { 2536 LOG(Media, "HTMLMediaElement::enterFullscreen"); 2537 #if ENABLE(FULLSCREEN_API) 2538 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { 2539 webkitRequestFullScreen(0); 2540 return; 2541 } 2542 #endif 2543 ASSERT(!m_isFullscreen); 2544 m_isFullscreen = true; 2545 if (hasMediaControls()) 2546 mediaControls()->enteredFullscreen(); 2547 if (document() && document()->page()) { 2548 document()->page()->chrome()->client()->enterFullscreenForNode(this); 2549 scheduleEvent(eventNames().webkitbeginfullscreenEvent); 2550 } 2551 } 2552 2553 void HTMLMediaElement::exitFullscreen() 2554 { 2555 LOG(Media, "HTMLMediaElement::exitFullscreen"); 2556 #if ENABLE(FULLSCREEN_API) 2557 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { 2558 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) 2559 document()->webkitCancelFullScreen(); 2560 return; 2561 } 2562 #endif 2563 ASSERT(m_isFullscreen); 2564 m_isFullscreen = false; 2565 if (hasMediaControls()) 2566 mediaControls()->exitedFullscreen(); 2567 if (document() && document()->page()) { 2568 if (document()->page()->chrome()->requiresFullscreenForVideoPlayback()) 2569 pauseInternal(); 2570 document()->page()->chrome()->client()->exitFullscreenForNode(this); 2571 scheduleEvent(eventNames().webkitendfullscreenEvent); 2572 } 2573 } 2574 2575 PlatformMedia HTMLMediaElement::platformMedia() const 2576 { 2577 return m_player ? m_player->platformMedia() : NoPlatformMedia; 2578 } 2579 2580 #if USE(ACCELERATED_COMPOSITING) 2581 PlatformLayer* HTMLMediaElement::platformLayer() const 2582 { 2583 return m_player ? m_player->platformLayer() : 0; 2584 } 2585 #endif 2586 2587 bool HTMLMediaElement::hasClosedCaptions() const 2588 { 2589 return m_player && m_player->hasClosedCaptions(); 2590 } 2591 2592 bool HTMLMediaElement::closedCaptionsVisible() const 2593 { 2594 return m_closedCaptionsVisible; 2595 } 2596 2597 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) 2598 { 2599 LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible)); 2600 2601 if (!m_player ||!hasClosedCaptions()) 2602 return; 2603 2604 m_closedCaptionsVisible = closedCaptionVisible; 2605 m_player->setClosedCaptionsVisible(closedCaptionVisible); 2606 if (hasMediaControls()) 2607 mediaControls()->changedClosedCaptionsVisibility(); 2608 } 2609 2610 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible) 2611 { 2612 setClosedCaptionsVisible(visible); 2613 } 2614 2615 bool HTMLMediaElement::webkitClosedCaptionsVisible() const 2616 { 2617 return closedCaptionsVisible(); 2618 } 2619 2620 2621 bool HTMLMediaElement::webkitHasClosedCaptions() const 2622 { 2623 return hasClosedCaptions(); 2624 } 2625 2626 #if ENABLE(MEDIA_STATISTICS) 2627 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const 2628 { 2629 if (!m_player) 2630 return 0; 2631 return m_player->audioDecodedByteCount(); 2632 } 2633 2634 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const 2635 { 2636 if (!m_player) 2637 return 0; 2638 return m_player->videoDecodedByteCount(); 2639 } 2640 #endif 2641 2642 void HTMLMediaElement::mediaCanStart() 2643 { 2644 LOG(Media, "HTMLMediaElement::mediaCanStart"); 2645 2646 ASSERT(m_isWaitingUntilMediaCanStart); 2647 m_isWaitingUntilMediaCanStart = false; 2648 loadInternal(); 2649 } 2650 2651 bool HTMLMediaElement::isURLAttribute(Attribute* attribute) const 2652 { 2653 return attribute->name() == srcAttr; 2654 } 2655 2656 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay) 2657 { 2658 if (m_shouldDelayLoadEvent == shouldDelay) 2659 return; 2660 2661 LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay)); 2662 2663 m_shouldDelayLoadEvent = shouldDelay; 2664 if (shouldDelay) 2665 document()->incrementLoadEventDelayCount(); 2666 else 2667 document()->decrementLoadEventDelayCount(); 2668 } 2669 2670 2671 void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites) 2672 { 2673 MediaPlayer::getSitesInMediaCache(sites); 2674 } 2675 2676 void HTMLMediaElement::clearMediaCache() 2677 { 2678 MediaPlayer::clearMediaCache(); 2679 } 2680 2681 void HTMLMediaElement::clearMediaCacheForSite(const String& site) 2682 { 2683 MediaPlayer::clearMediaCacheForSite(site); 2684 } 2685 2686 void HTMLMediaElement::privateBrowsingStateDidChange() 2687 { 2688 if (!m_player) 2689 return; 2690 2691 Settings* settings = document()->settings(); 2692 bool privateMode = !settings || settings->privateBrowsingEnabled(); 2693 LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode)); 2694 m_player->setPrivateBrowsingMode(privateMode); 2695 } 2696 2697 MediaControls* HTMLMediaElement::mediaControls() 2698 { 2699 return toMediaControls(shadowRoot()->firstChild()); 2700 } 2701 2702 bool HTMLMediaElement::hasMediaControls() 2703 { 2704 if (!shadowRoot()) 2705 return false; 2706 2707 Node* node = shadowRoot()->firstChild(); 2708 return node && node->isMediaControls(); 2709 } 2710 2711 void HTMLMediaElement::ensureMediaControls() 2712 { 2713 if (hasMediaControls()) 2714 return; 2715 2716 ExceptionCode ec; 2717 ensureShadowRoot()->appendChild(MediaControls::create(this), ec); 2718 } 2719 2720 void* HTMLMediaElement::preDispatchEventHandler(Event* event) 2721 { 2722 if (event && event->type() == eventNames().webkitfullscreenchangeEvent) { 2723 if (controls()) { 2724 if (!hasMediaControls()) { 2725 ensureMediaControls(); 2726 mediaControls()->reset(); 2727 } 2728 mediaControls()->show(); 2729 } else if (hasMediaControls()) 2730 mediaControls()->hide(); 2731 } 2732 return 0; 2733 } 2734 2735 2736 } 2737 2738 #endif 2739