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