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 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 189 m_restrictions |= RequireUserGestureForRateChangeRestriction; 190 #endif 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 1040 if (m_autoplaying && m_paused && autoplay()) { 1041 m_paused = false; 1042 invalidateCachedTime(); 1043 scheduleEvent(eventNames().playEvent); 1044 scheduleEvent(eventNames().playingEvent); 1045 } 1046 1047 shouldUpdateDisplayState = true; 1048 } 1049 1050 if (shouldUpdateDisplayState) 1051 updateDisplayState(); 1052 1053 updatePlayState(); 1054 } 1055 1056 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) 1057 { 1058 ASSERT(m_player); 1059 if (m_networkState != NETWORK_LOADING) 1060 return; 1061 1062 unsigned progress = m_player->bytesLoaded(); 1063 double time = WTF::currentTime(); 1064 double timedelta = time - m_previousProgressTime; 1065 1066 if (progress == m_previousProgress) { 1067 if (timedelta > 3.0 && !m_sentStalledEvent) { 1068 scheduleEvent(eventNames().stalledEvent); 1069 m_sentStalledEvent = true; 1070 setShouldDelayLoadEvent(false); 1071 } 1072 } else { 1073 scheduleEvent(eventNames().progressEvent); 1074 m_previousProgress = progress; 1075 m_previousProgressTime = time; 1076 m_sentStalledEvent = false; 1077 if (renderer()) 1078 renderer()->updateFromElement(); 1079 } 1080 } 1081 1082 void HTMLMediaElement::rewind(float timeDelta) 1083 { 1084 LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta); 1085 1086 ExceptionCode e; 1087 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e); 1088 } 1089 1090 void HTMLMediaElement::returnToRealtime() 1091 { 1092 LOG(Media, "HTMLMediaElement::returnToRealtime"); 1093 ExceptionCode e; 1094 setCurrentTime(maxTimeSeekable(), e); 1095 } 1096 1097 void HTMLMediaElement::addPlayedRange(float start, float end) 1098 { 1099 LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end); 1100 if (!m_playedTimeRanges) 1101 m_playedTimeRanges = TimeRanges::create(); 1102 m_playedTimeRanges->add(start, end); 1103 } 1104 1105 bool HTMLMediaElement::supportsSave() const 1106 { 1107 return m_player ? m_player->supportsSave() : false; 1108 } 1109 1110 void HTMLMediaElement::seek(float time, ExceptionCode& ec) 1111 { 1112 LOG(Media, "HTMLMediaElement::seek(%f)", time); 1113 1114 // 4.8.9.9 Seeking 1115 1116 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception. 1117 if (m_readyState == HAVE_NOTHING || !m_player) { 1118 ec = INVALID_STATE_ERR; 1119 return; 1120 } 1121 1122 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set. 1123 refreshCachedTime(); 1124 float now = currentTime(); 1125 1126 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is 1127 // already running. Abort that other instance of the algorithm without waiting for the step that 1128 // it is running to complete. 1129 // Nothing specific to be done here. 1130 1131 // 3 - Set the seeking IDL attribute to true. 1132 // The flag will be cleared when the engine tells us the time has actually changed. 1133 m_seeking = true; 1134 1135 // 5 - If the new playback position is later than the end of the media resource, then let it be the end 1136 // of the media resource instead. 1137 time = min(time, duration()); 1138 1139 // 6 - If the new playback position is less than the earliest possible position, let it be that position instead. 1140 float earliestTime = m_player->startTime(); 1141 time = max(time, earliestTime); 1142 1143 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This 1144 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's 1145 // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and 1146 // not generate a timechanged callback. This means m_seeking will never be cleared and we will never 1147 // fire a 'seeked' event. 1148 #if !LOG_DISABLED 1149 float mediaTime = m_player->mediaTimeForTimeValue(time); 1150 if (time != mediaTime) 1151 LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime); 1152 #endif 1153 time = m_player->mediaTimeForTimeValue(time); 1154 1155 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the 1156 // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute 1157 // that is the nearest to the new playback position. ... If there are no ranges given in the seekable 1158 // attribute then set the seeking IDL attribute to false and abort these steps. 1159 RefPtr<TimeRanges> seekableRanges = seekable(); 1160 1161 // Short circuit seeking to the current time by just firing the events if no seek is required. 1162 // Don't skip calling the media engine if we are in poster mode because a seek should always 1163 // cancel poster display. 1164 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster); 1165 if (noSeekRequired) { 1166 if (time == now) { 1167 scheduleEvent(eventNames().seekingEvent); 1168 scheduleTimeupdateEvent(false); 1169 scheduleEvent(eventNames().seekedEvent); 1170 } 1171 m_seeking = false; 1172 return; 1173 } 1174 time = seekableRanges->nearest(time); 1175 1176 if (m_playing) { 1177 if (m_lastSeekTime < now) 1178 addPlayedRange(m_lastSeekTime, now); 1179 } 1180 m_lastSeekTime = time; 1181 m_sentEndEvent = false; 1182 1183 // 8 - Set the current playback position to the given new playback position 1184 m_player->seek(time); 1185 1186 // 9 - Queue a task to fire a simple event named seeking at the element. 1187 scheduleEvent(eventNames().seekingEvent); 1188 1189 // 10 - Queue a task to fire a simple event named timeupdate at the element. 1190 scheduleTimeupdateEvent(false); 1191 1192 // 11-15 are handled, if necessary, when the engine signals a readystate change. 1193 } 1194 1195 void HTMLMediaElement::finishSeek() 1196 { 1197 LOG(Media, "HTMLMediaElement::finishSeek"); 1198 1199 // 4.8.10.9 Seeking step 14 1200 m_seeking = false; 1201 1202 // 4.8.10.9 Seeking step 15 1203 scheduleEvent(eventNames().seekedEvent); 1204 1205 setDisplayMode(Video); 1206 } 1207 1208 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const 1209 { 1210 return m_readyState; 1211 } 1212 1213 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const 1214 { 1215 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown; 1216 } 1217 1218 bool HTMLMediaElement::hasAudio() const 1219 { 1220 return m_player ? m_player->hasAudio() : false; 1221 } 1222 1223 bool HTMLMediaElement::seeking() const 1224 { 1225 return m_seeking; 1226 } 1227 1228 void HTMLMediaElement::refreshCachedTime() const 1229 { 1230 m_cachedTime = m_player->currentTime(); 1231 m_cachedTimeWallClockUpdateTime = WTF::currentTime(); 1232 } 1233 1234 void HTMLMediaElement::invalidateCachedTime() 1235 { 1236 LOG(Media, "HTMLMediaElement::invalidateCachedTime"); 1237 1238 // Don't try to cache movie time when playback first starts as the time reported by the engine 1239 // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it 1240 // too early. 1241 static const double minimumTimePlayingBeforeCacheSnapshot = 0.5; 1242 1243 m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot; 1244 m_cachedTime = invalidMediaTime; 1245 } 1246 1247 // playback state 1248 float HTMLMediaElement::currentTime() const 1249 { 1250 #if LOG_CACHED_TIME_WARNINGS 1251 static const double minCachedDeltaForWarning = 0.01; 1252 #endif 1253 1254 if (!m_player) 1255 return 0; 1256 1257 if (m_seeking) { 1258 LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime); 1259 return m_lastSeekTime; 1260 } 1261 1262 if (m_cachedTime != invalidMediaTime && m_paused) { 1263 #if LOG_CACHED_TIME_WARNINGS 1264 float delta = m_cachedTime - m_player->currentTime(); 1265 if (delta > minCachedDeltaForWarning) 1266 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta); 1267 #endif 1268 return m_cachedTime; 1269 } 1270 1271 // Is it too soon use a cached time? 1272 double now = WTF::currentTime(); 1273 double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime(); 1274 1275 if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) { 1276 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; 1277 1278 // Not too soon, use the cached time only if it hasn't expired. 1279 if (wallClockDelta < maximumDurationToCacheMediaTime) { 1280 float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta)); 1281 1282 #if LOG_CACHED_TIME_WARNINGS 1283 float delta = adjustedCacheTime - m_player->currentTime(); 1284 if (delta > minCachedDeltaForWarning) 1285 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta); 1286 #endif 1287 return adjustedCacheTime; 1288 } 1289 } 1290 1291 #if LOG_CACHED_TIME_WARNINGS 1292 if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) { 1293 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; 1294 float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime(); 1295 LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta); 1296 } 1297 #endif 1298 1299 refreshCachedTime(); 1300 1301 return m_cachedTime; 1302 } 1303 1304 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec) 1305 { 1306 seek(time, ec); 1307 } 1308 1309 float HTMLMediaElement::startTime() const 1310 { 1311 if (!m_player) 1312 return 0; 1313 return m_player->startTime(); 1314 } 1315 1316 float HTMLMediaElement::duration() const 1317 { 1318 if (m_player && m_readyState >= HAVE_METADATA) 1319 return m_player->duration(); 1320 1321 return numeric_limits<float>::quiet_NaN(); 1322 } 1323 1324 bool HTMLMediaElement::paused() const 1325 { 1326 return m_paused; 1327 } 1328 1329 float HTMLMediaElement::defaultPlaybackRate() const 1330 { 1331 return m_defaultPlaybackRate; 1332 } 1333 1334 void HTMLMediaElement::setDefaultPlaybackRate(float rate) 1335 { 1336 if (m_defaultPlaybackRate != rate) { 1337 m_defaultPlaybackRate = rate; 1338 scheduleEvent(eventNames().ratechangeEvent); 1339 } 1340 } 1341 1342 float HTMLMediaElement::playbackRate() const 1343 { 1344 return m_playbackRate; 1345 } 1346 1347 void HTMLMediaElement::setPlaybackRate(float rate) 1348 { 1349 LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate); 1350 1351 if (m_playbackRate != rate) { 1352 m_playbackRate = rate; 1353 invalidateCachedTime(); 1354 scheduleEvent(eventNames().ratechangeEvent); 1355 } 1356 if (m_player && potentiallyPlaying() && m_player->rate() != rate) 1357 m_player->setRate(rate); 1358 } 1359 1360 bool HTMLMediaElement::webkitPreservesPitch() const 1361 { 1362 return m_webkitPreservesPitch; 1363 } 1364 1365 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) 1366 { 1367 LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch)); 1368 1369 m_webkitPreservesPitch = preservesPitch; 1370 1371 if (!m_player) 1372 return; 1373 1374 m_player->setPreservesPitch(preservesPitch); 1375 } 1376 1377 bool HTMLMediaElement::ended() const 1378 { 1379 // 4.8.10.8 Playing the media resource 1380 // The ended attribute must return true if the media element has ended 1381 // playback and the direction of playback is forwards, and false otherwise. 1382 return endedPlayback() && m_playbackRate > 0; 1383 } 1384 1385 bool HTMLMediaElement::autoplay() const 1386 { 1387 return hasAttribute(autoplayAttr); 1388 } 1389 1390 void HTMLMediaElement::setAutoplay(bool b) 1391 { 1392 LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b)); 1393 setBooleanAttribute(autoplayAttr, b); 1394 } 1395 1396 String HTMLMediaElement::preload() const 1397 { 1398 switch (m_preload) { 1399 case MediaPlayer::None: 1400 return "none"; 1401 break; 1402 case MediaPlayer::MetaData: 1403 return "metadata"; 1404 break; 1405 case MediaPlayer::Auto: 1406 return "auto"; 1407 break; 1408 } 1409 1410 ASSERT_NOT_REACHED(); 1411 return String(); 1412 } 1413 1414 void HTMLMediaElement::setPreload(const String& preload) 1415 { 1416 LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data()); 1417 setAttribute(preloadAttr, preload); 1418 } 1419 1420 void HTMLMediaElement::play(bool isUserGesture) 1421 { 1422 LOG(Media, "HTMLMediaElement::play(isUserGesture : %s)", boolString(isUserGesture)); 1423 1424 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture 1425 #if PLATFORM(ANDROID) 1426 && !m_userGestureInitiated 1427 #endif 1428 ) 1429 return; 1430 1431 #if PLATFORM(ANDROID) 1432 // B/c we set the restriction to require gesture for rate change for 1433 // Android, when we don't early return, we can safely set this to true. 1434 m_userGestureInitiated = true; 1435 #endif 1436 1437 Document* doc = document(); 1438 Settings* settings = doc->settings(); 1439 if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) { 1440 // It should be impossible to be processing the canplay event while handling a user gesture 1441 // since it is dispatched asynchronously. 1442 ASSERT(!isUserGesture); 1443 String host = doc->baseURL().host(); 1444 if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org")) 1445 return; 1446 } 1447 1448 playInternal(); 1449 } 1450 1451 void HTMLMediaElement::playInternal() 1452 { 1453 LOG(Media, "HTMLMediaElement::playInternal"); 1454 1455 // 4.8.10.9. Playing the media resource 1456 if (!m_player || m_networkState == NETWORK_EMPTY) 1457 scheduleLoad(); 1458 1459 if (endedPlayback()) { 1460 ExceptionCode unused; 1461 seek(0, unused); 1462 } 1463 1464 if (m_paused) { 1465 m_paused = false; 1466 invalidateCachedTime(); 1467 scheduleEvent(eventNames().playEvent); 1468 1469 if (m_readyState <= HAVE_CURRENT_DATA) 1470 scheduleEvent(eventNames().waitingEvent); 1471 else if (m_readyState >= HAVE_FUTURE_DATA) 1472 scheduleEvent(eventNames().playingEvent); 1473 } 1474 m_autoplaying = false; 1475 1476 updatePlayState(); 1477 } 1478 1479 void HTMLMediaElement::pause(bool isUserGesture) 1480 { 1481 LOG(Media, "HTMLMediaElement::pause(isUserGesture : %s)", boolString(isUserGesture)); 1482 1483 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture 1484 #if PLATFORM(ANDROID) 1485 && !m_userGestureInitiated 1486 #endif 1487 ) 1488 return; 1489 #if PLATFORM(ANDROID) 1490 // B/c we set the restriction to require gesture for rate change for 1491 // Android, when we don't early return, we can safely set this to true. 1492 m_userGestureInitiated = true; 1493 #endif 1494 pauseInternal(); 1495 } 1496 1497 1498 void HTMLMediaElement::pauseInternal() 1499 { 1500 LOG(Media, "HTMLMediaElement::pauseInternal"); 1501 1502 // 4.8.10.9. Playing the media resource 1503 if (!m_player || m_networkState == NETWORK_EMPTY) 1504 scheduleLoad(); 1505 1506 m_autoplaying = false; 1507 1508 if (!m_paused) { 1509 m_paused = true; 1510 scheduleTimeupdateEvent(false); 1511 scheduleEvent(eventNames().pauseEvent); 1512 } 1513 1514 updatePlayState(); 1515 } 1516 1517 bool HTMLMediaElement::loop() const 1518 { 1519 return hasAttribute(loopAttr); 1520 } 1521 1522 void HTMLMediaElement::setLoop(bool b) 1523 { 1524 LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b)); 1525 setBooleanAttribute(loopAttr, b); 1526 } 1527 1528 bool HTMLMediaElement::controls() const 1529 { 1530 Frame* frame = document()->frame(); 1531 1532 // always show controls when scripting is disabled 1533 if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript)) 1534 return true; 1535 1536 // always show controls for video when fullscreen playback is required. 1537 if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback()) 1538 return true; 1539 1540 // Always show controls when in full screen mode. 1541 if (isFullscreen()) 1542 return true; 1543 1544 return hasAttribute(controlsAttr); 1545 } 1546 1547 void HTMLMediaElement::setControls(bool b) 1548 { 1549 LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b)); 1550 setBooleanAttribute(controlsAttr, b); 1551 } 1552 1553 float HTMLMediaElement::volume() const 1554 { 1555 return m_volume; 1556 } 1557 1558 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) 1559 { 1560 LOG(Media, "HTMLMediaElement::setVolume(%f)", vol); 1561 1562 if (vol < 0.0f || vol > 1.0f) { 1563 ec = INDEX_SIZE_ERR; 1564 return; 1565 } 1566 1567 if (m_volume != vol) { 1568 m_volume = vol; 1569 updateVolume(); 1570 scheduleEvent(eventNames().volumechangeEvent); 1571 } 1572 } 1573 1574 bool HTMLMediaElement::muted() const 1575 { 1576 return m_muted; 1577 } 1578 1579 void HTMLMediaElement::setMuted(bool muted) 1580 { 1581 LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted)); 1582 1583 if (m_muted != muted) { 1584 m_muted = muted; 1585 // Avoid recursion when the player reports volume changes. 1586 if (!processingMediaPlayerCallback()) { 1587 if (m_player) { 1588 m_player->setMuted(m_muted); 1589 if (hasMediaControls()) 1590 mediaControls()->changedMute(); 1591 } 1592 } 1593 scheduleEvent(eventNames().volumechangeEvent); 1594 } 1595 } 1596 1597 void HTMLMediaElement::togglePlayState() 1598 { 1599 LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay())); 1600 1601 // We can safely call the internal play/pause methods, which don't check restrictions, because 1602 // this method is only called from the built-in media controller 1603 if (canPlay()) { 1604 setPlaybackRate(defaultPlaybackRate()); 1605 playInternal(); 1606 } else 1607 pauseInternal(); 1608 } 1609 1610 void HTMLMediaElement::beginScrubbing() 1611 { 1612 LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused())); 1613 1614 if (!paused()) { 1615 if (ended()) { 1616 // Because a media element stays in non-paused state when it reaches end, playback resumes 1617 // when the slider is dragged from the end to another position unless we pause first. Do 1618 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. 1619 pause(processingUserGesture()); 1620 } else { 1621 // Not at the end but we still want to pause playback so the media engine doesn't try to 1622 // continue playing during scrubbing. Pause without generating an event as we will 1623 // unpause after scrubbing finishes. 1624 setPausedInternal(true); 1625 } 1626 } 1627 } 1628 1629 void HTMLMediaElement::endScrubbing() 1630 { 1631 LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal)); 1632 1633 if (m_pausedInternal) 1634 setPausedInternal(false); 1635 } 1636 1637 // The spec says to fire periodic timeupdate events (those sent while playing) every 1638 // "15 to 250ms", we choose the slowest frequency 1639 static const double maxTimeupdateEventFrequency = 0.25; 1640 1641 void HTMLMediaElement::startPlaybackProgressTimer() 1642 { 1643 if (m_playbackProgressTimer.isActive()) 1644 return; 1645 1646 m_previousProgressTime = WTF::currentTime(); 1647 m_previousProgress = 0; 1648 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); 1649 } 1650 1651 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) 1652 { 1653 ASSERT(m_player); 1654 if (!m_playbackRate) 1655 return; 1656 1657 scheduleTimeupdateEvent(true); 1658 if (hasMediaControls()) { 1659 #if PLATFORM(ANDROID) 1660 m_mouseOver = WTF::currentTime() - m_lastTouch <= TOUCH_DELAY; 1661 #endif 1662 if (!m_mouseOver && controls() && hasVideo()) 1663 mediaControls()->makeTransparent(); 1664 1665 mediaControls()->playbackProgressed(); 1666 } 1667 // FIXME: deal with cue ranges here 1668 } 1669 1670 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) 1671 { 1672 double now = WTF::currentTime(); 1673 double timedelta = now - m_lastTimeUpdateEventWallTime; 1674 1675 // throttle the periodic events 1676 if (periodicEvent && timedelta < maxTimeupdateEventFrequency) 1677 return; 1678 1679 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one 1680 // event at a given time so filter here 1681 float movieTime = currentTime(); 1682 if (movieTime != m_lastTimeUpdateEventMovieTime) { 1683 scheduleEvent(eventNames().timeupdateEvent); 1684 m_lastTimeUpdateEventWallTime = now; 1685 m_lastTimeUpdateEventMovieTime = movieTime; 1686 } 1687 } 1688 1689 bool HTMLMediaElement::canPlay() const 1690 { 1691 return paused() || ended() || m_readyState < HAVE_METADATA; 1692 } 1693 1694 float HTMLMediaElement::percentLoaded() const 1695 { 1696 if (!m_player) 1697 return 0; 1698 float duration = m_player->duration(); 1699 1700 if (!duration || isinf(duration)) 1701 return 0; 1702 1703 float buffered = 0; 1704 RefPtr<TimeRanges> timeRanges = m_player->buffered(); 1705 for (unsigned i = 0; i < timeRanges->length(); ++i) { 1706 ExceptionCode ignoredException; 1707 float start = timeRanges->start(i, ignoredException); 1708 float end = timeRanges->end(i, ignoredException); 1709 buffered += end - start; 1710 } 1711 return buffered / duration; 1712 } 1713 1714 bool HTMLMediaElement::havePotentialSourceChild() 1715 { 1716 // Stash the current <source> node and next nodes so we can restore them after checking 1717 // to see there is another potential. 1718 HTMLSourceElement* currentSourceNode = m_currentSourceNode; 1719 Node* nextNode = m_nextChildNodeToConsider; 1720 1721 KURL nextURL = selectNextSourceChild(0, DoNothing); 1722 1723 m_currentSourceNode = currentSourceNode; 1724 m_nextChildNodeToConsider = nextNode; 1725 1726 return nextURL.isValid(); 1727 } 1728 1729 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid) 1730 { 1731 #if !LOG_DISABLED 1732 // Don't log if this was just called to find out if there are any valid <source> elements. 1733 bool shouldLog = actionIfInvalid != DoNothing; 1734 if (shouldLog) 1735 LOG(Media, "HTMLMediaElement::selectNextSourceChild(contentType : \"%s\")", contentType ? contentType->raw().utf8().data() : ""); 1736 #endif 1737 1738 if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) { 1739 #if !LOG_DISABLED 1740 if (shouldLog) 1741 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\""); 1742 #endif 1743 return KURL(); 1744 } 1745 1746 KURL mediaURL; 1747 Node* node; 1748 HTMLSourceElement* source = 0; 1749 bool lookingForStartNode = m_nextChildNodeToConsider; 1750 bool canUse = false; 1751 1752 for (node = firstChild(); !canUse && node; node = node->nextSibling()) { 1753 if (lookingForStartNode && m_nextChildNodeToConsider != node) 1754 continue; 1755 lookingForStartNode = false; 1756 1757 if (!node->hasTagName(sourceTag)) 1758 continue; 1759 1760 source = static_cast<HTMLSourceElement*>(node); 1761 1762 // 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 1763 mediaURL = source->getNonEmptyURLAttribute(srcAttr); 1764 #if !LOG_DISABLED 1765 if (shouldLog) 1766 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data()); 1767 #endif 1768 if (mediaURL.isEmpty()) 1769 goto check_again; 1770 1771 if (source->hasAttribute(mediaAttr)) { 1772 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); 1773 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); 1774 #if !LOG_DISABLED 1775 if (shouldLog) 1776 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data()); 1777 #endif 1778 if (!screenEval.eval(media.get())) 1779 goto check_again; 1780 } 1781 1782 if (source->hasAttribute(typeAttr)) { 1783 #if !LOG_DISABLED 1784 if (shouldLog) 1785 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data()); 1786 #endif 1787 if (!MediaPlayer::supportsType(ContentType(source->type()))) 1788 goto check_again; 1789 } 1790 1791 // Is it safe to load this url? 1792 if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string())) 1793 goto check_again; 1794 1795 // Making it this far means the <source> looks reasonable. 1796 canUse = true; 1797 1798 check_again: 1799 if (!canUse && actionIfInvalid == Complain) 1800 source->scheduleErrorEvent(); 1801 } 1802 1803 if (canUse) { 1804 if (contentType) 1805 *contentType = ContentType(source->type()); 1806 m_currentSourceNode = source; 1807 m_nextChildNodeToConsider = source->nextSibling(); 1808 if (!m_nextChildNodeToConsider) 1809 m_nextChildNodeToConsider = sourceChildEndOfListValue(); 1810 } else { 1811 m_currentSourceNode = 0; 1812 m_nextChildNodeToConsider = sourceChildEndOfListValue(); 1813 } 1814 1815 #if !LOG_DISABLED 1816 if (shouldLog) 1817 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL.string()).utf8().data() : ""); 1818 #endif 1819 return canUse ? mediaURL : KURL(); 1820 } 1821 1822 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source) 1823 { 1824 LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source); 1825 1826 #if !LOG_DISABLED 1827 if (source->hasTagName(sourceTag)) { 1828 KURL url = source->getNonEmptyURLAttribute(srcAttr); 1829 LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data()); 1830 } 1831 #endif 1832 1833 // We should only consider a <source> element when there is not src attribute at all. 1834 if (hasAttribute(srcAttr)) 1835 return; 1836 1837 // 4.8.8 - If a source element is inserted as a child of a media element that has no src 1838 // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke 1839 // the media element's resource selection algorithm. 1840 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) { 1841 scheduleLoad(); 1842 return; 1843 } 1844 1845 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) { 1846 LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source"); 1847 m_nextChildNodeToConsider = source; 1848 return; 1849 } 1850 1851 if (m_nextChildNodeToConsider != sourceChildEndOfListValue()) 1852 return; 1853 1854 // 4.8.9.5, resource selection algorithm, source elements section: 1855 // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.) 1856 // 21 - Asynchronously await a stable state... 1857 // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case 1858 // it hasn't been fired yet). 1859 setShouldDelayLoadEvent(true); 1860 1861 // 23 - Set the networkState back to NETWORK_LOADING. 1862 m_networkState = NETWORK_LOADING; 1863 1864 // 24 - Jump back to the find next candidate step above. 1865 m_nextChildNodeToConsider = source; 1866 scheduleNextSourceChild(); 1867 } 1868 1869 void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source) 1870 { 1871 LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source); 1872 1873 #if !LOG_DISABLED 1874 if (source->hasTagName(sourceTag)) { 1875 KURL url = source->getNonEmptyURLAttribute(srcAttr); 1876 LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data()); 1877 } 1878 #endif 1879 1880 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider) 1881 return; 1882 1883 if (source == m_nextChildNodeToConsider) { 1884 m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling(); 1885 if (!m_nextChildNodeToConsider) 1886 m_nextChildNodeToConsider = sourceChildEndOfListValue(); 1887 LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider); 1888 } else if (source == m_currentSourceNode) { 1889 // Clear the current source node pointer, but don't change the movie as the spec says: 1890 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already 1891 // inserted in a video or audio element will have no effect. 1892 m_currentSourceNode = 0; 1893 LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0"); 1894 } 1895 } 1896 1897 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) 1898 { 1899 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged"); 1900 1901 beginProcessingMediaPlayerCallback(); 1902 1903 invalidateCachedTime(); 1904 1905 // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek. 1906 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA) 1907 finishSeek(); 1908 1909 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, 1910 // it will only queue a 'timeupdate' event if we haven't already posted one at the current 1911 // movie time. 1912 scheduleTimeupdateEvent(false); 1913 1914 float now = currentTime(); 1915 float dur = duration(); 1916 if (!isnan(dur) && dur && now >= dur) { 1917 if (loop()) { 1918 ExceptionCode ignoredException; 1919 m_sentEndEvent = false; 1920 seek(0, ignoredException); 1921 } else { 1922 if (!m_sentEndEvent) { 1923 m_sentEndEvent = true; 1924 scheduleEvent(eventNames().endedEvent); 1925 } 1926 } 1927 } 1928 else 1929 m_sentEndEvent = false; 1930 1931 updatePlayState(); 1932 endProcessingMediaPlayerCallback(); 1933 } 1934 1935 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) 1936 { 1937 LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged"); 1938 1939 beginProcessingMediaPlayerCallback(); 1940 if (m_player) { 1941 float vol = m_player->volume(); 1942 if (vol != m_volume) { 1943 m_volume = vol; 1944 updateVolume(); 1945 scheduleEvent(eventNames().volumechangeEvent); 1946 } 1947 } 1948 endProcessingMediaPlayerCallback(); 1949 } 1950 1951 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) 1952 { 1953 LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged"); 1954 1955 beginProcessingMediaPlayerCallback(); 1956 if (m_player) 1957 setMuted(m_player->muted()); 1958 endProcessingMediaPlayerCallback(); 1959 } 1960 1961 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) 1962 { 1963 LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged"); 1964 1965 beginProcessingMediaPlayerCallback(); 1966 scheduleEvent(eventNames().durationchangeEvent); 1967 if (renderer()) 1968 renderer()->updateFromElement(); 1969 endProcessingMediaPlayerCallback(); 1970 1971 #if PLATFORM(ANDROID) 1972 if (hasMediaControls()) 1973 mediaControls()->reset(); 1974 #endif 1975 } 1976 1977 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) 1978 { 1979 LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged"); 1980 1981 beginProcessingMediaPlayerCallback(); 1982 1983 invalidateCachedTime(); 1984 1985 // Stash the rate in case the one we tried to set isn't what the engine is 1986 // using (eg. it can't handle the rate we set) 1987 m_playbackRate = m_player->rate(); 1988 invalidateCachedTime(); 1989 endProcessingMediaPlayerCallback(); 1990 } 1991 1992 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*) 1993 { 1994 LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged"); 1995 1996 if (!m_player || m_pausedInternal) 1997 return; 1998 1999 beginProcessingMediaPlayerCallback(); 2000 if (m_player->paused()) 2001 pauseInternal(); 2002 else 2003 playInternal(); 2004 endProcessingMediaPlayerCallback(); 2005 } 2006 2007 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) 2008 { 2009 LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks"); 2010 2011 // The MediaPlayer came across content it cannot completely handle. 2012 // This is normally acceptable except when we are in a standalone 2013 // MediaDocument. If so, tell the document what has happened. 2014 if (ownerDocument()->isMediaDocument()) { 2015 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument()); 2016 mediaDocument->mediaElementSawUnsupportedTracks(); 2017 } 2018 } 2019 2020 // MediaPlayerPresentation methods 2021 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) 2022 { 2023 beginProcessingMediaPlayerCallback(); 2024 updateDisplayState(); 2025 if (renderer()) 2026 renderer()->repaint(); 2027 endProcessingMediaPlayerCallback(); 2028 } 2029 2030 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) 2031 { 2032 LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged"); 2033 2034 beginProcessingMediaPlayerCallback(); 2035 if (renderer()) 2036 renderer()->updateFromElement(); 2037 endProcessingMediaPlayerCallback(); 2038 } 2039 2040 #if USE(ACCELERATED_COMPOSITING) 2041 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) 2042 { 2043 if (renderer() && renderer()->isVideo()) { 2044 ASSERT(renderer()->view()); 2045 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer())); 2046 } 2047 return false; 2048 } 2049 2050 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*) 2051 { 2052 LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged"); 2053 2054 // Kick off a fake recalcStyle that will update the compositing tree. 2055 setNeedsStyleRecalc(SyntheticStyleChange); 2056 } 2057 #endif 2058 2059 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*) 2060 { 2061 LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated"); 2062 beginProcessingMediaPlayerCallback(); 2063 if (renderer()) 2064 renderer()->updateFromElement(); 2065 endProcessingMediaPlayerCallback(); 2066 } 2067 2068 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*) 2069 { 2070 LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable"); 2071 beginProcessingMediaPlayerCallback(); 2072 if (displayMode() == PosterWaitingForVideo) { 2073 setDisplayMode(Video); 2074 #if USE(ACCELERATED_COMPOSITING) 2075 mediaPlayerRenderingModeChanged(m_player.get()); 2076 #endif 2077 } 2078 endProcessingMediaPlayerCallback(); 2079 } 2080 2081 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const 2082 { 2083 if (!m_player) 2084 return TimeRanges::create(); 2085 return m_player->buffered(); 2086 } 2087 2088 PassRefPtr<TimeRanges> HTMLMediaElement::played() 2089 { 2090 if (m_playing) { 2091 float time = currentTime(); 2092 if (time > m_lastSeekTime) 2093 addPlayedRange(m_lastSeekTime, time); 2094 } 2095 2096 if (!m_playedTimeRanges) 2097 m_playedTimeRanges = TimeRanges::create(); 2098 2099 return m_playedTimeRanges->copy(); 2100 } 2101 2102 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const 2103 { 2104 // FIXME real ranges support 2105 if (!maxTimeSeekable()) 2106 return TimeRanges::create(); 2107 return TimeRanges::create(minTimeSeekable(), maxTimeSeekable()); 2108 } 2109 2110 bool HTMLMediaElement::potentiallyPlaying() const 2111 { 2112 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing 2113 // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the 2114 // checks in couldPlayIfEnoughData(). 2115 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA; 2116 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData(); 2117 } 2118 2119 bool HTMLMediaElement::couldPlayIfEnoughData() const 2120 { 2121 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); 2122 } 2123 2124 bool HTMLMediaElement::endedPlayback() const 2125 { 2126 float dur = duration(); 2127 if (!m_player || isnan(dur)) 2128 return false; 2129 2130 // 4.8.10.8 Playing the media resource 2131 2132 // A media element is said to have ended playback when the element's 2133 // readyState attribute is HAVE_METADATA or greater, 2134 if (m_readyState < HAVE_METADATA) 2135 return false; 2136 2137 // and the current playback position is the end of the media resource and the direction 2138 // of playback is forwards and the media element does not have a loop attribute specified, 2139 float now = currentTime(); 2140 if (m_playbackRate > 0) 2141 return dur > 0 && now >= dur && !loop(); 2142 2143 // or the current playback position is the earliest possible position and the direction 2144 // of playback is backwards 2145 if (m_playbackRate < 0) 2146 return now <= 0; 2147 2148 return false; 2149 } 2150 2151 bool HTMLMediaElement::stoppedDueToErrors() const 2152 { 2153 if (m_readyState >= HAVE_METADATA && m_error) { 2154 RefPtr<TimeRanges> seekableRanges = seekable(); 2155 if (!seekableRanges->contain(currentTime())) 2156 return true; 2157 } 2158 2159 return false; 2160 } 2161 2162 bool HTMLMediaElement::pausedForUserInteraction() const 2163 { 2164 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] 2165 return false; 2166 } 2167 2168 float HTMLMediaElement::minTimeSeekable() const 2169 { 2170 return 0; 2171 } 2172 2173 float HTMLMediaElement::maxTimeSeekable() const 2174 { 2175 return m_player ? m_player->maxTimeSeekable() : 0; 2176 } 2177 2178 void HTMLMediaElement::updateVolume() 2179 { 2180 if (!m_player) 2181 return; 2182 2183 // Avoid recursion when the player reports volume changes. 2184 if (!processingMediaPlayerCallback()) { 2185 Page* page = document()->page(); 2186 float volumeMultiplier = page ? page->mediaVolume() : 1; 2187 2188 m_player->setMuted(m_muted); 2189 m_player->setVolume(m_volume * volumeMultiplier); 2190 } 2191 2192 if (hasMediaControls()) 2193 mediaControls()->changedVolume(); 2194 } 2195 2196 void HTMLMediaElement::updatePlayState() 2197 { 2198 if (!m_player) 2199 return; 2200 2201 if (m_pausedInternal) { 2202 if (!m_player->paused()) 2203 m_player->pause(); 2204 refreshCachedTime(); 2205 m_playbackProgressTimer.stop(); 2206 if (hasMediaControls()) 2207 mediaControls()->playbackStopped(); 2208 return; 2209 } 2210 2211 bool shouldBePlaying = potentiallyPlaying(); 2212 bool playerPaused = m_player->paused(); 2213 2214 LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s", 2215 boolString(shouldBePlaying), boolString(playerPaused)); 2216 2217 if (shouldBePlaying) { 2218 setDisplayMode(Video); 2219 invalidateCachedTime(); 2220 2221 if (playerPaused) { 2222 if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback()) 2223 enterFullscreen(); 2224 2225 // Set rate, muted before calling play in case they were set before the media engine was setup. 2226 // The media engine should just stash the rate and muted values since it isn't already playing. 2227 m_player->setRate(m_playbackRate); 2228 m_player->setMuted(m_muted); 2229 2230 m_player->play(); 2231 } 2232 2233 if (hasMediaControls()) 2234 mediaControls()->playbackStarted(); 2235 startPlaybackProgressTimer(); 2236 m_playing = true; 2237 2238 } else { // Should not be playing right now 2239 if (!playerPaused) 2240 m_player->pause(); 2241 refreshCachedTime(); 2242 2243 m_playbackProgressTimer.stop(); 2244 m_playing = false; 2245 float time = currentTime(); 2246 if (time > m_lastSeekTime) 2247 addPlayedRange(m_lastSeekTime, time); 2248 2249 if (couldPlayIfEnoughData()) 2250 m_player->prepareToPlay(); 2251 2252 if (hasMediaControls()) 2253 mediaControls()->playbackStopped(); 2254 } 2255 2256 if (renderer()) 2257 renderer()->updateFromElement(); 2258 } 2259 2260 void HTMLMediaElement::setPausedInternal(bool b) 2261 { 2262 m_pausedInternal = b; 2263 updatePlayState(); 2264 } 2265 2266 void HTMLMediaElement::stopPeriodicTimers() 2267 { 2268 m_progressEventTimer.stop(); 2269 m_playbackProgressTimer.stop(); 2270 } 2271 2272 void HTMLMediaElement::userCancelledLoad() 2273 { 2274 LOG(Media, "HTMLMediaElement::userCancelledLoad"); 2275 2276 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded) 2277 return; 2278 2279 // If the media data fetching process is aborted by the user: 2280 2281 // 1 - The user agent should cancel the fetching process. 2282 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2283 m_player.clear(); 2284 #endif 2285 stopPeriodicTimers(); 2286 m_loadTimer.stop(); 2287 m_loadState = WaitingForSource; 2288 2289 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED. 2290 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); 2291 2292 // 3 - Queue a task to fire a simple event named error at the media element. 2293 scheduleEvent(eventNames().abortEvent); 2294 2295 // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the 2296 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a 2297 // simple event named emptied at the element. Otherwise, set the element's networkState 2298 // attribute to the NETWORK_IDLE value. 2299 if (m_readyState == HAVE_NOTHING) { 2300 m_networkState = NETWORK_EMPTY; 2301 scheduleEvent(eventNames().emptiedEvent); 2302 } 2303 else 2304 m_networkState = NETWORK_IDLE; 2305 2306 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 2307 setShouldDelayLoadEvent(false); 2308 2309 // 6 - Abort the overall resource selection algorithm. 2310 m_currentSourceNode = 0; 2311 2312 // Reset m_readyState since m_player is gone. 2313 m_readyState = HAVE_NOTHING; 2314 } 2315 2316 bool HTMLMediaElement::canSuspend() const 2317 { 2318 return true; 2319 } 2320 2321 void HTMLMediaElement::stop() 2322 { 2323 LOG(Media, "HTMLMediaElement::stop"); 2324 if (m_isFullscreen) 2325 exitFullscreen(); 2326 2327 m_inActiveDocument = false; 2328 userCancelledLoad(); 2329 2330 // Stop the playback without generating events 2331 setPausedInternal(true); 2332 2333 if (renderer()) 2334 renderer()->updateFromElement(); 2335 2336 stopPeriodicTimers(); 2337 cancelPendingEventsAndCallbacks(); 2338 } 2339 2340 void HTMLMediaElement::suspend(ReasonForSuspension why) 2341 { 2342 LOG(Media, "HTMLMediaElement::suspend"); 2343 2344 switch (why) 2345 { 2346 case DocumentWillBecomeInactive: 2347 stop(); 2348 break; 2349 case JavaScriptDebuggerPaused: 2350 case WillShowDialog: 2351 // Do nothing, we don't pause media playback in these cases. 2352 break; 2353 } 2354 } 2355 2356 void HTMLMediaElement::resume() 2357 { 2358 LOG(Media, "HTMLMediaElement::resume"); 2359 2360 m_inActiveDocument = true; 2361 setPausedInternal(false); 2362 2363 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { 2364 // Restart the load if it was aborted in the middle by moving the document to the page cache. 2365 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to 2366 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). 2367 // This behavior is not specified but it seems like a sensible thing to do. 2368 ExceptionCode ec; 2369 load(processingUserGesture(), ec); 2370 } 2371 2372 if (renderer()) 2373 renderer()->updateFromElement(); 2374 } 2375 2376 bool HTMLMediaElement::hasPendingActivity() const 2377 { 2378 // Return true when we have pending events so we can't fire events after the JS 2379 // object gets collected. 2380 bool pending = m_pendingEvents.size(); 2381 LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending)); 2382 return pending; 2383 } 2384 2385 void HTMLMediaElement::mediaVolumeDidChange() 2386 { 2387 LOG(Media, "HTMLMediaElement::mediaVolumeDidChange"); 2388 updateVolume(); 2389 } 2390 2391 void HTMLMediaElement::defaultEventHandler(Event* event) 2392 { 2393 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2394 RenderObject* r = renderer(); 2395 if (!r || !r->isWidget()) 2396 return; 2397 2398 Widget* widget = toRenderWidget(r)->widget(); 2399 if (widget) 2400 widget->handleEvent(event); 2401 #else 2402 if (event->isMouseEvent()) { 2403 #if PLATFORM(ANDROID) 2404 m_lastTouch = WTF::currentTime(); 2405 #endif 2406 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); 2407 if (mouseEvent->relatedTarget() != this) { 2408 if (event->type() == eventNames().mouseoverEvent) { 2409 m_mouseOver = true; 2410 if (hasMediaControls() && controls() && !canPlay()) 2411 mediaControls()->makeOpaque(); 2412 } else if (event->type() == eventNames().mouseoutEvent) 2413 m_mouseOver = false; 2414 } 2415 } 2416 2417 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) 2418 if (event->isTouchEvent()) { 2419 m_mouseOver = !(event->type() == eventNames().touchendEvent || event->type() == eventNames().touchcancelEvent); 2420 if (m_mouseOver && hasMediaControls() && controls() && !canPlay()) { 2421 m_lastTouch = WTF::currentTime(); 2422 mediaControls()->makeOpaque(); 2423 } 2424 } 2425 #endif 2426 2427 #if PLATFORM(ANDROID) 2428 // It is really hard to hit the play/pause button on mobile devices. 2429 // This allows user to click the video area to toggle play/pause state. 2430 if (event->type() == eventNames().clickEvent 2431 && !hasEventListeners(eventNames().clickEvent)) { 2432 m_userGestureInitiated = processingUserGesture(); 2433 togglePlayState(); 2434 } 2435 #endif 2436 HTMLElement::defaultEventHandler(event); 2437 #endif 2438 } 2439 2440 bool HTMLMediaElement::processingUserGesture() const 2441 { 2442 Frame* frame = document()->frame(); 2443 FrameLoader* loader = frame ? frame->loader() : 0; 2444 2445 // return 'true' for safety if we don't know the answer 2446 return loader ? loader->isProcessingUserGesture() : true; 2447 } 2448 2449 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2450 2451 void HTMLMediaElement::ensureMediaPlayer() 2452 { 2453 if (!m_player) 2454 m_player = MediaPlayer::create(this); 2455 } 2456 2457 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) 2458 { 2459 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { 2460 togglePlayState(); 2461 return; 2462 } 2463 2464 if (m_player) 2465 m_player->deliverNotification(notification); 2466 } 2467 2468 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) 2469 { 2470 ensureMediaPlayer(); 2471 m_player->setMediaPlayerProxy(proxy); 2472 } 2473 2474 void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values) 2475 { 2476 Frame* frame = document()->frame(); 2477 FrameLoader* loader = frame ? frame->loader() : 0; 2478 2479 if (isVideo()) { 2480 KURL posterURL = getNonEmptyURLAttribute(posterAttr); 2481 if (!posterURL.isEmpty() && loader && loader->willLoadMediaElementURL(posterURL)) { 2482 names.append("_media_element_poster_"); 2483 values.append(posterURL.string()); 2484 } 2485 } 2486 2487 if (controls()) { 2488 names.append("_media_element_controls_"); 2489 values.append("true"); 2490 } 2491 2492 url = src(); 2493 if (!isSafeToLoadURL(url, Complain)) 2494 url = selectNextSourceChild(0, DoNothing); 2495 2496 m_currentSrc = url.string(); 2497 if (url.isValid() && loader && loader->willLoadMediaElementURL(url)) { 2498 names.append("_media_element_src_"); 2499 values.append(m_currentSrc); 2500 } 2501 } 2502 2503 void HTMLMediaElement::finishParsingChildren() 2504 { 2505 HTMLElement::finishParsingChildren(); 2506 document()->updateStyleIfNeeded(); 2507 createMediaPlayerProxy(); 2508 } 2509 2510 void HTMLMediaElement::createMediaPlayerProxy() 2511 { 2512 ensureMediaPlayer(); 2513 2514 if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate)) 2515 return; 2516 2517 Frame* frame = document()->frame(); 2518 FrameLoader* loader = frame ? frame->loader() : 0; 2519 if (!loader) 2520 return; 2521 2522 LOG(Media, "HTMLMediaElement::createMediaPlayerProxy"); 2523 2524 KURL url; 2525 Vector<String> paramNames; 2526 Vector<String> paramValues; 2527 2528 getPluginProxyParams(url, paramNames, paramValues); 2529 2530 // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to 2531 // display:none 2532 m_proxyWidget = loader->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues); 2533 if (m_proxyWidget) 2534 m_needWidgetUpdate = false; 2535 } 2536 2537 void HTMLMediaElement::updateWidget(PluginCreationOption) 2538 { 2539 mediaElement->setNeedWidgetUpdate(false); 2540 2541 Vector<String> paramNames; 2542 Vector<String> paramValues; 2543 KURL kurl; 2544 2545 mediaElement->getPluginProxyParams(kurl, paramNames, paramValues); 2546 SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); 2547 loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues); 2548 } 2549 2550 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) 2551 2552 bool HTMLMediaElement::isFullscreen() const 2553 { 2554 if (m_isFullscreen) 2555 return true; 2556 2557 #if ENABLE(FULLSCREEN_API) 2558 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) 2559 return true; 2560 #endif 2561 2562 return false; 2563 } 2564 2565 void HTMLMediaElement::enterFullscreen() 2566 { 2567 LOG(Media, "HTMLMediaElement::enterFullscreen"); 2568 #if ENABLE(FULLSCREEN_API) 2569 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { 2570 webkitRequestFullScreen(0); 2571 return; 2572 } 2573 #endif 2574 ASSERT(!m_isFullscreen); 2575 m_isFullscreen = true; 2576 if (hasMediaControls()) 2577 mediaControls()->enteredFullscreen(); 2578 if (document() && document()->page()) { 2579 document()->page()->chrome()->client()->enterFullscreenForNode(this); 2580 scheduleEvent(eventNames().webkitbeginfullscreenEvent); 2581 } 2582 } 2583 2584 void HTMLMediaElement::exitFullscreen() 2585 { 2586 LOG(Media, "HTMLMediaElement::exitFullscreen"); 2587 #if ENABLE(FULLSCREEN_API) 2588 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { 2589 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) 2590 document()->webkitCancelFullScreen(); 2591 return; 2592 } 2593 #endif 2594 ASSERT(m_isFullscreen); 2595 m_isFullscreen = false; 2596 if (hasMediaControls()) 2597 mediaControls()->exitedFullscreen(); 2598 if (document() && document()->page()) { 2599 if (document()->page()->chrome()->requiresFullscreenForVideoPlayback()) 2600 pauseInternal(); 2601 document()->page()->chrome()->client()->exitFullscreenForNode(this); 2602 scheduleEvent(eventNames().webkitendfullscreenEvent); 2603 } 2604 } 2605 2606 PlatformMedia HTMLMediaElement::platformMedia() const 2607 { 2608 return m_player ? m_player->platformMedia() : NoPlatformMedia; 2609 } 2610 2611 #if USE(ACCELERATED_COMPOSITING) 2612 PlatformLayer* HTMLMediaElement::platformLayer() const 2613 { 2614 return m_player ? m_player->platformLayer() : 0; 2615 } 2616 #endif 2617 2618 bool HTMLMediaElement::hasClosedCaptions() const 2619 { 2620 return m_player && m_player->hasClosedCaptions(); 2621 } 2622 2623 bool HTMLMediaElement::closedCaptionsVisible() const 2624 { 2625 return m_closedCaptionsVisible; 2626 } 2627 2628 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) 2629 { 2630 LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible)); 2631 2632 if (!m_player ||!hasClosedCaptions()) 2633 return; 2634 2635 m_closedCaptionsVisible = closedCaptionVisible; 2636 m_player->setClosedCaptionsVisible(closedCaptionVisible); 2637 if (hasMediaControls()) 2638 mediaControls()->changedClosedCaptionsVisibility(); 2639 } 2640 2641 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible) 2642 { 2643 setClosedCaptionsVisible(visible); 2644 } 2645 2646 bool HTMLMediaElement::webkitClosedCaptionsVisible() const 2647 { 2648 return closedCaptionsVisible(); 2649 } 2650 2651 2652 bool HTMLMediaElement::webkitHasClosedCaptions() const 2653 { 2654 return hasClosedCaptions(); 2655 } 2656 2657 #if ENABLE(MEDIA_STATISTICS) 2658 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const 2659 { 2660 if (!m_player) 2661 return 0; 2662 return m_player->audioDecodedByteCount(); 2663 } 2664 2665 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const 2666 { 2667 if (!m_player) 2668 return 0; 2669 return m_player->videoDecodedByteCount(); 2670 } 2671 #endif 2672 2673 void HTMLMediaElement::mediaCanStart() 2674 { 2675 LOG(Media, "HTMLMediaElement::mediaCanStart"); 2676 2677 ASSERT(m_isWaitingUntilMediaCanStart); 2678 m_isWaitingUntilMediaCanStart = false; 2679 loadInternal(); 2680 } 2681 2682 bool HTMLMediaElement::isURLAttribute(Attribute* attribute) const 2683 { 2684 return attribute->name() == srcAttr; 2685 } 2686 2687 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay) 2688 { 2689 if (m_shouldDelayLoadEvent == shouldDelay) 2690 return; 2691 2692 LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay)); 2693 2694 m_shouldDelayLoadEvent = shouldDelay; 2695 if (shouldDelay) 2696 document()->incrementLoadEventDelayCount(); 2697 else 2698 document()->decrementLoadEventDelayCount(); 2699 } 2700 2701 2702 void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites) 2703 { 2704 MediaPlayer::getSitesInMediaCache(sites); 2705 } 2706 2707 void HTMLMediaElement::clearMediaCache() 2708 { 2709 MediaPlayer::clearMediaCache(); 2710 } 2711 2712 void HTMLMediaElement::clearMediaCacheForSite(const String& site) 2713 { 2714 MediaPlayer::clearMediaCacheForSite(site); 2715 } 2716 2717 void HTMLMediaElement::privateBrowsingStateDidChange() 2718 { 2719 if (!m_player) 2720 return; 2721 2722 Settings* settings = document()->settings(); 2723 bool privateMode = !settings || settings->privateBrowsingEnabled(); 2724 LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode)); 2725 m_player->setPrivateBrowsingMode(privateMode); 2726 } 2727 2728 MediaControls* HTMLMediaElement::mediaControls() 2729 { 2730 return toMediaControls(shadowRoot()->firstChild()); 2731 } 2732 2733 bool HTMLMediaElement::hasMediaControls() 2734 { 2735 if (!shadowRoot()) 2736 return false; 2737 2738 Node* node = shadowRoot()->firstChild(); 2739 return node && node->isMediaControls(); 2740 } 2741 2742 bool HTMLMediaElement::createMediaControls() 2743 { 2744 if (hasMediaControls()) 2745 return true; 2746 2747 ExceptionCode ec; 2748 RefPtr<MediaControls> controls = MediaControls::create(this); 2749 if (!controls) 2750 return false; 2751 2752 ensureShadowRoot()->appendChild(controls, ec); 2753 return true; 2754 } 2755 2756 void* HTMLMediaElement::preDispatchEventHandler(Event* event) 2757 { 2758 if (event && event->type() == eventNames().webkitfullscreenchangeEvent) { 2759 if (controls()) { 2760 if (!hasMediaControls()) { 2761 if (!createMediaControls()) 2762 return 0; 2763 2764 mediaControls()->reset(); 2765 } 2766 mediaControls()->show(); 2767 } else if (hasMediaControls()) 2768 mediaControls()->hide(); 2769 } 2770 return 0; 2771 } 2772 2773 2774 } 2775 2776 #endif 2777