1 /* 2 * Copyright (C) 2007, 2008, 2009, 2010 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 "Chrome.h" 32 #include "ChromeClient.h" 33 #include "ClientRect.h" 34 #include "ClientRectList.h" 35 #include "CSSHelper.h" 36 #include "CSSPropertyNames.h" 37 #include "CSSValueKeywords.h" 38 #include "ContentType.h" 39 #include "DocLoader.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 "MIMETypeRegistry.h" 52 #include "MappedAttribute.h" 53 #include "MediaDocument.h" 54 #include "MediaError.h" 55 #include "MediaList.h" 56 #include "MediaPlayer.h" 57 #include "MediaQueryEvaluator.h" 58 #include "Page.h" 59 #include "RenderVideo.h" 60 #include "RenderView.h" 61 #include "ScriptEventListener.h" 62 #include "TimeRanges.h" 63 #include <limits> 64 #include <wtf/CurrentTime.h> 65 #include <wtf/MathExtras.h> 66 67 #if USE(ACCELERATED_COMPOSITING) 68 #include "RenderView.h" 69 #include "RenderLayerCompositor.h" 70 #endif 71 72 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 73 #include "RenderPartObject.h" 74 #include "Widget.h" 75 #endif 76 77 using namespace std; 78 79 namespace WebCore { 80 81 using namespace HTMLNames; 82 83 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc) 84 : HTMLElement(tagName, doc) 85 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) 86 , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired) 87 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) 88 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired) 89 , m_playedTimeRanges() 90 , m_playbackRate(1.0f) 91 , m_defaultPlaybackRate(1.0f) 92 , m_webkitPreservesPitch(true) 93 , m_networkState(NETWORK_EMPTY) 94 , m_readyState(HAVE_NOTHING) 95 , m_volume(1.0f) 96 , m_lastSeekTime(0) 97 , m_previousProgress(0) 98 , m_previousProgressTime(numeric_limits<double>::max()) 99 , m_lastTimeUpdateEventWallTime(0) 100 , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max()) 101 , m_loadState(WaitingForSource) 102 , m_currentSourceNode(0) 103 , m_player(0) 104 , m_restrictions(NoRestrictions) 105 , m_playing(false) 106 , m_processingMediaPlayerCallback(0) 107 , m_processingLoad(false) 108 , m_delayingTheLoadEvent(false) 109 , m_haveFiredLoadedData(false) 110 , m_inActiveDocument(true) 111 , m_autoplaying(true) 112 , m_muted(false) 113 , m_paused(true) 114 , m_seeking(false) 115 , m_sentStalledEvent(false) 116 , m_sentEndEvent(false) 117 , m_pausedInternal(false) 118 , m_sendProgressEvents(true) 119 , m_isFullscreen(false) 120 , m_closedCaptionsVisible(false) 121 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 122 , m_needWidgetUpdate(false) 123 #endif 124 { 125 document()->registerForDocumentActivationCallbacks(this); 126 document()->registerForMediaVolumeCallbacks(this); 127 } 128 129 HTMLMediaElement::~HTMLMediaElement() 130 { 131 document()->unregisterForDocumentActivationCallbacks(this); 132 document()->unregisterForMediaVolumeCallbacks(this); 133 } 134 135 void HTMLMediaElement::willMoveToNewOwnerDocument() 136 { 137 document()->unregisterForDocumentActivationCallbacks(this); 138 document()->unregisterForMediaVolumeCallbacks(this); 139 HTMLElement::willMoveToNewOwnerDocument(); 140 } 141 142 void HTMLMediaElement::didMoveToNewOwnerDocument() 143 { 144 document()->registerForDocumentActivationCallbacks(this); 145 document()->registerForMediaVolumeCallbacks(this); 146 HTMLElement::didMoveToNewOwnerDocument(); 147 } 148 149 150 bool HTMLMediaElement::checkDTD(const Node* newChild) 151 { 152 return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild); 153 } 154 155 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls) 156 { 157 HTMLElement::attributeChanged(attr, preserveDecls); 158 159 const QualifiedName& attrName = attr->name(); 160 if (attrName == srcAttr) { 161 // don't have a src or any <source> children, trigger load 162 if (inDocument() && m_loadState == WaitingForSource) 163 scheduleLoad(); 164 } 165 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 166 else if (attrName == controlsAttr) { 167 if (!isVideo() && attached() && (controls() != (renderer() != 0))) { 168 detach(); 169 attach(); 170 } 171 if (renderer()) 172 renderer()->updateFromElement(); 173 } 174 #endif 175 } 176 177 void HTMLMediaElement::parseMappedAttribute(MappedAttribute* attr) 178 { 179 const QualifiedName& attrName = attr->name(); 180 181 if (attrName == autobufferAttr) { 182 if (m_player) 183 m_player->setAutobuffer(!attr->isNull()); 184 } else if (attrName == onabortAttr) 185 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr)); 186 else if (attrName == onbeforeloadAttr) 187 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); 188 else if (attrName == oncanplayAttr) 189 setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr)); 190 else if (attrName == oncanplaythroughAttr) 191 setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr)); 192 else if (attrName == ondurationchangeAttr) 193 setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr)); 194 else if (attrName == onemptiedAttr) 195 setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr)); 196 else if (attrName == onendedAttr) 197 setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr)); 198 else if (attrName == onerrorAttr) 199 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); 200 else if (attrName == onloadAttr) 201 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); 202 else if (attrName == onloadeddataAttr) 203 setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr)); 204 else if (attrName == onloadedmetadataAttr) 205 setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr)); 206 else if (attrName == onloadstartAttr) 207 setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr)); 208 else if (attrName == onpauseAttr) 209 setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr)); 210 else if (attrName == onplayAttr) 211 setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr)); 212 else if (attrName == onplayingAttr) 213 setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr)); 214 else if (attrName == onprogressAttr) 215 setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr)); 216 else if (attrName == onratechangeAttr) 217 setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr)); 218 else if (attrName == onseekedAttr) 219 setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr)); 220 else if (attrName == onseekingAttr) 221 setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr)); 222 else if (attrName == onstalledAttr) 223 setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr)); 224 else if (attrName == onsuspendAttr) 225 setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr)); 226 else if (attrName == ontimeupdateAttr) 227 setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr)); 228 else if (attrName == onvolumechangeAttr) 229 setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr)); 230 else if (attrName == onwaitingAttr) 231 setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr)); 232 else if (attrName == onwebkitbeginfullscreenAttr) 233 setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr)); 234 else if (attrName == onwebkitendfullscreenAttr) 235 setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr)); 236 else 237 HTMLElement::parseMappedAttribute(attr); 238 } 239 240 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style) 241 { 242 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 243 UNUSED_PARAM(style); 244 Frame* frame = document()->frame(); 245 if (!frame) 246 return false; 247 248 return true; 249 #else 250 return controls() ? HTMLElement::rendererIsNeeded(style) : false; 251 #endif 252 } 253 254 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*) 255 { 256 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 257 return new (arena) RenderEmbeddedObject(this); 258 #else 259 return new (arena) RenderMedia(this); 260 #endif 261 } 262 263 void HTMLMediaElement::insertedIntoDocument() 264 { 265 HTMLElement::insertedIntoDocument(); 266 if (!src().isEmpty() && m_networkState == NETWORK_EMPTY) 267 scheduleLoad(); 268 } 269 270 void HTMLMediaElement::removedFromDocument() 271 { 272 if (m_networkState > NETWORK_EMPTY) 273 pause(processingUserGesture()); 274 if (m_isFullscreen) 275 exitFullscreen(); 276 HTMLElement::removedFromDocument(); 277 } 278 279 void HTMLMediaElement::attach() 280 { 281 ASSERT(!attached()); 282 283 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 284 m_needWidgetUpdate = true; 285 #endif 286 287 HTMLElement::attach(); 288 289 if (renderer()) 290 renderer()->updateFromElement(); 291 } 292 293 void HTMLMediaElement::recalcStyle(StyleChange change) 294 { 295 HTMLElement::recalcStyle(change); 296 297 if (renderer()) 298 renderer()->updateFromElement(); 299 } 300 301 void HTMLMediaElement::scheduleLoad() 302 { 303 if (m_loadTimer.isActive()) 304 return; 305 prepareForLoad(); 306 m_loadTimer.startOneShot(0); 307 } 308 309 void HTMLMediaElement::scheduleNextSourceChild() 310 { 311 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad. 312 m_loadTimer.startOneShot(0); 313 } 314 315 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) 316 { 317 m_pendingEvents.append(Event::create(eventName, false, true)); 318 if (!m_asyncEventTimer.isActive()) 319 m_asyncEventTimer.startOneShot(0); 320 } 321 322 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*) 323 { 324 Vector<RefPtr<Event> > pendingEvents; 325 ExceptionCode ec = 0; 326 327 m_pendingEvents.swap(pendingEvents); 328 unsigned count = pendingEvents.size(); 329 for (unsigned ndx = 0; ndx < count; ++ndx) 330 dispatchEvent(pendingEvents[ndx].release(), ec); 331 } 332 333 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) 334 { 335 if (m_loadState == LoadingFromSourceElement) 336 loadNextSourceChild(); 337 else 338 loadInternal(); 339 } 340 341 static String serializeTimeOffset(float time) 342 { 343 String timeString = String::number(time); 344 // FIXME serialize time offset values properly (format not specified yet) 345 timeString.append("s"); 346 return timeString; 347 } 348 349 static float parseTimeOffset(const String& timeString, bool* ok = 0) 350 { 351 const UChar* characters = timeString.characters(); 352 unsigned length = timeString.length(); 353 354 if (length && characters[length - 1] == 's') 355 length--; 356 357 // FIXME parse time offset values (format not specified yet) 358 float val = charactersToFloat(characters, length, ok); 359 return val; 360 } 361 362 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const 363 { 364 bool ok; 365 String timeString = getAttribute(name); 366 float result = parseTimeOffset(timeString, &ok); 367 if (ok) 368 return result; 369 return valueOnError; 370 } 371 372 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value) 373 { 374 setAttribute(name, serializeTimeOffset(value)); 375 } 376 377 PassRefPtr<MediaError> HTMLMediaElement::error() const 378 { 379 return m_error; 380 } 381 382 KURL HTMLMediaElement::src() const 383 { 384 return document()->completeURL(getAttribute(srcAttr)); 385 } 386 387 void HTMLMediaElement::setSrc(const String& url) 388 { 389 setAttribute(srcAttr, url); 390 } 391 392 String HTMLMediaElement::currentSrc() const 393 { 394 return m_currentSrc; 395 } 396 397 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const 398 { 399 return m_networkState; 400 } 401 402 String HTMLMediaElement::canPlayType(const String& mimeType) const 403 { 404 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType)); 405 String canPlay; 406 407 // 4.8.10.3 408 switch (support) 409 { 410 case MediaPlayer::IsNotSupported: 411 canPlay = ""; 412 break; 413 case MediaPlayer::MayBeSupported: 414 canPlay = "maybe"; 415 break; 416 case MediaPlayer::IsSupported: 417 canPlay = "probably"; 418 break; 419 } 420 421 return canPlay; 422 } 423 424 void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec) 425 { 426 if (m_restrictions & RequireUserGestureForLoadRestriction && !isUserGesture) 427 ec = INVALID_STATE_ERR; 428 else { 429 prepareForLoad(); 430 loadInternal(); 431 } 432 } 433 434 void HTMLMediaElement::prepareForLoad() 435 { 436 // Perform the cleanup required for the resource load algorithm to run. 437 stopPeriodicTimers(); 438 m_loadTimer.stop(); 439 m_sentStalledEvent = false; 440 m_haveFiredLoadedData = false; 441 442 // 2 - Abort any already-running instance of the resource selection algorithm for this element. 443 m_currentSourceNode = 0; 444 445 // 3 - If there are any tasks from the media element's media element event task source in 446 // one of the task queues, then remove those tasks. 447 cancelPendingEventsAndCallbacks(); 448 } 449 450 void HTMLMediaElement::loadInternal() 451 { 452 // If the load() method for this element is already being invoked, then abort these steps. 453 if (m_processingLoad) 454 return; 455 m_processingLoad = true; 456 457 // Steps 1 and 2 were done in prepareForLoad() 458 459 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue 460 // a task to fire a simple event named abort at the media element. 461 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) 462 scheduleEvent(eventNames().abortEvent); 463 464 // 4 465 if (m_networkState != NETWORK_EMPTY) { 466 m_networkState = NETWORK_EMPTY; 467 m_readyState = HAVE_NOTHING; 468 m_paused = true; 469 m_seeking = false; 470 if (m_player) { 471 m_player->pause(); 472 m_playing = false; 473 m_player->seek(0); 474 } 475 scheduleEvent(eventNames().emptiedEvent); 476 } 477 478 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute. 479 setPlaybackRate(defaultPlaybackRate()); 480 481 // 6 - Set the error attribute to null and the autoplaying flag to true. 482 m_error = 0; 483 m_autoplaying = true; 484 485 m_playedTimeRanges = TimeRanges::create(); 486 m_lastSeekTime = 0; 487 m_closedCaptionsVisible = false; 488 489 // 7 - Invoke the media element's resource selection algorithm. 490 selectMediaResource(); 491 m_processingLoad = false; 492 } 493 494 void HTMLMediaElement::selectMediaResource() 495 { 496 // 1 - Set the networkState to NETWORK_NO_SOURCE 497 m_networkState = NETWORK_NO_SOURCE; 498 499 // 2 - Asynchronously await a stable state. 500 501 // 3 - ... the media element has neither a src attribute ... 502 String mediaSrc = getAttribute(srcAttr); 503 if (!mediaSrc) { 504 // ... nor a source element child: ... 505 Node* node; 506 for (node = firstChild(); node; node = node->nextSibling()) { 507 if (node->hasTagName(sourceTag)) 508 break; 509 } 510 511 if (!node) { 512 m_loadState = WaitingForSource; 513 514 // ... set the networkState to NETWORK_EMPTY, and abort these steps 515 m_networkState = NETWORK_EMPTY; 516 ASSERT(!m_delayingTheLoadEvent); 517 return; 518 } 519 } 520 521 // 4 522 m_delayingTheLoadEvent = true; 523 m_networkState = NETWORK_LOADING; 524 525 // 5 526 scheduleEvent(eventNames().loadstartEvent); 527 528 // 6 - If the media element has a src attribute, then run these substeps 529 ContentType contentType(""); 530 if (!mediaSrc.isNull()) { 531 KURL mediaURL = document()->completeURL(mediaSrc); 532 if (isSafeToLoadURL(mediaURL, Complain) && dispatchBeforeLoadEvent(mediaURL.string())) { 533 m_loadState = LoadingFromSrcAttr; 534 loadResource(mediaURL, contentType); 535 } else 536 noneSupported(); 537 538 return; 539 } 540 541 // Otherwise, the source elements will be used 542 m_currentSourceNode = 0; 543 loadNextSourceChild(); 544 } 545 546 void HTMLMediaElement::loadNextSourceChild() 547 { 548 ContentType contentType(""); 549 KURL mediaURL = selectNextSourceChild(&contentType, Complain); 550 if (!mediaURL.isValid()) { 551 waitForSourceChange(); 552 return; 553 } 554 555 m_loadState = LoadingFromSourceElement; 556 loadResource(mediaURL, contentType); 557 } 558 559 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType) 560 { 561 ASSERT(isSafeToLoadURL(initialURL, Complain)); 562 563 Frame* frame = document()->frame(); 564 if (!frame) 565 return; 566 FrameLoader* loader = frame->loader(); 567 if (!loader) 568 return; 569 570 KURL url(initialURL); 571 if (!loader->willLoadMediaElementURL(url)) 572 return; 573 574 // The resource fetch algorithm 575 m_networkState = NETWORK_LOADING; 576 577 m_currentSrc = url; 578 579 if (m_sendProgressEvents) 580 startProgressEventTimer(); 581 582 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 583 m_player = MediaPlayer::create(this); 584 #else 585 if (!m_player) 586 m_player = MediaPlayer::create(this); 587 #endif 588 589 m_player->setAutobuffer(autobuffer()); 590 m_player->setPreservesPitch(m_webkitPreservesPitch); 591 updateVolume(); 592 593 #if PLATFORM(ANDROID) 594 if (isVideo()) 595 m_player->setMediaElementType(MediaPlayer::Video); 596 else 597 m_player->setMediaElementType(MediaPlayer::Audio); 598 #endif 599 m_player->load(m_currentSrc, contentType); 600 601 if (isVideo() && m_player->canLoadPoster()) { 602 KURL posterUrl = static_cast<HTMLVideoElement*>(this)->poster(); 603 if (!posterUrl.isEmpty()) 604 m_player->setPoster(posterUrl); 605 } 606 607 if (renderer()) 608 renderer()->updateFromElement(); 609 } 610 611 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid) 612 { 613 Frame* frame = document()->frame(); 614 FrameLoader* loader = frame ? frame->loader() : 0; 615 616 // don't allow remote to local urls, and check with the frame loader client. 617 if (!loader || !SecurityOrigin::canLoad(url, String(), document())) { 618 if (actionIfInvalid == Complain) 619 FrameLoader::reportLocalLoadFailed(frame, url.string()); 620 return false; 621 } 622 623 return true; 624 } 625 626 void HTMLMediaElement::startProgressEventTimer() 627 { 628 if (m_progressEventTimer.isActive()) 629 return; 630 631 m_previousProgressTime = WTF::currentTime(); 632 m_previousProgress = 0; 633 // 350ms is not magic, it is in the spec! 634 m_progressEventTimer.startRepeating(0.350); 635 } 636 637 void HTMLMediaElement::waitForSourceChange() 638 { 639 stopPeriodicTimers(); 640 m_loadState = WaitingForSource; 641 642 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value 643 m_networkState = NETWORK_NO_SOURCE; 644 645 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 646 m_delayingTheLoadEvent = false; 647 } 648 649 void HTMLMediaElement::noneSupported() 650 { 651 stopPeriodicTimers(); 652 m_loadState = WaitingForSource; 653 m_currentSourceNode = 0; 654 655 // 5 - Reaching this step indicates that either the URL failed to resolve, or the media 656 // resource failed to load. Set the error attribute to a new MediaError object whose 657 // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED. 658 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); 659 660 // 6 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value. 661 m_networkState = NETWORK_NO_SOURCE; 662 663 // 7 - Queue a task to fire a progress event called error at the media element, in 664 // the context of the fetching process that was used to try to obtain the media 665 // resource in the resource fetch algorithm. 666 scheduleEvent(eventNames().errorEvent); 667 668 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 669 m_delayingTheLoadEvent = false; 670 671 // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource. 672 673 if (isVideo()) 674 static_cast<HTMLVideoElement*>(this)->updatePosterImage(); 675 if (renderer()) 676 renderer()->updateFromElement(); 677 } 678 679 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) 680 { 681 // 1 - The user agent should cancel the fetching process. 682 stopPeriodicTimers(); 683 m_loadState = WaitingForSource; 684 685 // 2 - Set the error attribute to a new MediaError object whose code attribute is 686 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE. 687 m_error = err; 688 689 // 3 - Queue a task to fire a progress event called error at the media element, in 690 // the context of the fetching process started by this instance of this algorithm. 691 scheduleEvent(eventNames().errorEvent); 692 693 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a 694 // task to fire a simple event called emptied at the element. 695 m_networkState = NETWORK_EMPTY; 696 scheduleEvent(eventNames().emptiedEvent); 697 698 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 699 m_delayingTheLoadEvent = false; 700 701 // 6 - Abort the overall resource selection algorithm. 702 m_currentSourceNode = 0; 703 } 704 705 void HTMLMediaElement::cancelPendingEventsAndCallbacks() 706 { 707 m_pendingEvents.clear(); 708 709 for (Node* node = firstChild(); node; node = node->nextSibling()) { 710 if (node->hasTagName(sourceTag)) 711 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent(); 712 } 713 } 714 715 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) 716 { 717 beginProcessingMediaPlayerCallback(); 718 setNetworkState(m_player->networkState()); 719 endProcessingMediaPlayerCallback(); 720 } 721 722 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) 723 { 724 if (state == MediaPlayer::Empty) { 725 // just update the cached state and leave, we can't do anything 726 m_networkState = NETWORK_EMPTY; 727 return; 728 } 729 730 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) { 731 stopPeriodicTimers(); 732 733 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more 734 // <source> children, schedule the next one 735 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) { 736 m_currentSourceNode->scheduleErrorEvent(); 737 if (havePotentialSourceChild()) 738 scheduleNextSourceChild(); 739 else 740 waitForSourceChange(); 741 742 return; 743 } 744 745 if (state == MediaPlayer::NetworkError) 746 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK)); 747 else if (state == MediaPlayer::DecodeError) 748 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE)); 749 else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr) 750 noneSupported(); 751 752 if (isVideo()) 753 static_cast<HTMLVideoElement*>(this)->updatePosterImage(); 754 755 return; 756 } 757 758 if (state == MediaPlayer::Idle) { 759 if (m_networkState > NETWORK_IDLE) { 760 stopPeriodicTimers(); 761 scheduleEvent(eventNames().suspendEvent); 762 } 763 m_networkState = NETWORK_IDLE; 764 } 765 766 if (state == MediaPlayer::Loading) { 767 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE) 768 startProgressEventTimer(); 769 m_networkState = NETWORK_LOADING; 770 } 771 772 if (state == MediaPlayer::Loaded) { 773 NetworkState oldState = m_networkState; 774 775 m_networkState = NETWORK_LOADED; 776 if (oldState < NETWORK_LOADED || oldState == NETWORK_NO_SOURCE) { 777 m_progressEventTimer.stop(); 778 779 // Schedule one last progress event so we guarantee that at least one is fired 780 // for files that load very quickly. 781 scheduleEvent(eventNames().progressEvent); 782 783 // Check to see if readyState changes need to be dealt with before sending the 784 // 'load' event so we report 'canplaythrough' first. This is necessary because a 785 // media engine reports readyState and networkState changes separately 786 MediaPlayer::ReadyState currentState = m_player->readyState(); 787 if (static_cast<ReadyState>(currentState) != m_readyState) 788 setReadyState(currentState); 789 790 scheduleEvent(eventNames().loadEvent); 791 } 792 } 793 } 794 795 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) 796 { 797 beginProcessingMediaPlayerCallback(); 798 799 setReadyState(m_player->readyState()); 800 801 endProcessingMediaPlayerCallback(); 802 } 803 804 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) 805 { 806 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it 807 bool wasPotentiallyPlaying = potentiallyPlaying(); 808 809 ReadyState oldState = m_readyState; 810 m_readyState = static_cast<ReadyState>(state); 811 812 if (m_readyState == oldState) 813 return; 814 815 if (m_networkState == NETWORK_EMPTY) 816 return; 817 818 if (m_seeking) { 819 // 4.8.10.10, step 8 820 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) 821 scheduleEvent(eventNames().waitingEvent); 822 823 // 4.8.10.10, step 9 824 if (m_readyState < HAVE_CURRENT_DATA) { 825 if (oldState >= HAVE_CURRENT_DATA) 826 scheduleEvent(eventNames().seekingEvent); 827 } else { 828 // 4.8.10.10 step 12 & 13. 829 finishSeek(); 830 } 831 832 } else { 833 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) { 834 // 4.8.10.9 835 scheduleTimeupdateEvent(false); 836 scheduleEvent(eventNames().waitingEvent); 837 } 838 } 839 840 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) { 841 scheduleEvent(eventNames().durationchangeEvent); 842 scheduleEvent(eventNames().loadedmetadataEvent); 843 844 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 845 if (renderer() && renderer()->isVideo()) { 846 toRenderVideo(renderer())->videoSizeChanged(); 847 } 848 #endif 849 m_delayingTheLoadEvent = false; 850 m_player->seek(0); 851 } 852 853 bool shouldUpdatePosterImage = false; 854 855 // 4.8.10.7 says loadeddata is sent only when the new state *is* HAVE_CURRENT_DATA: "If the 856 // previous ready state was HAVE_METADATA and the new ready state is HAVE_CURRENT_DATA", 857 // but the event table at the end of the spec says it is sent when: "readyState newly 858 // increased to HAVE_CURRENT_DATA or greater for the first time" 859 // We go with the later because it seems useful to count on getting this event 860 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) { 861 m_haveFiredLoadedData = true; 862 shouldUpdatePosterImage = true; 863 scheduleEvent(eventNames().loadeddataEvent); 864 } 865 866 bool isPotentiallyPlaying = potentiallyPlaying(); 867 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) { 868 scheduleEvent(eventNames().canplayEvent); 869 if (isPotentiallyPlaying) 870 scheduleEvent(eventNames().playingEvent); 871 shouldUpdatePosterImage = true; 872 } 873 874 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) { 875 if (oldState <= HAVE_CURRENT_DATA) 876 scheduleEvent(eventNames().canplayEvent); 877 878 scheduleEvent(eventNames().canplaythroughEvent); 879 880 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) 881 scheduleEvent(eventNames().playingEvent); 882 883 if (m_autoplaying && m_paused && autoplay()) { 884 m_paused = false; 885 scheduleEvent(eventNames().playEvent); 886 scheduleEvent(eventNames().playingEvent); 887 } 888 889 shouldUpdatePosterImage = true; 890 } 891 892 if (shouldUpdatePosterImage && isVideo()) 893 static_cast<HTMLVideoElement*>(this)->updatePosterImage(); 894 895 updatePlayState(); 896 } 897 898 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) 899 { 900 ASSERT(m_player); 901 if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) 902 return; 903 904 unsigned progress = m_player->bytesLoaded(); 905 double time = WTF::currentTime(); 906 double timedelta = time - m_previousProgressTime; 907 908 if (progress == m_previousProgress) { 909 if (timedelta > 3.0 && !m_sentStalledEvent) { 910 scheduleEvent(eventNames().stalledEvent); 911 m_sentStalledEvent = true; 912 } 913 } else { 914 scheduleEvent(eventNames().progressEvent); 915 m_previousProgress = progress; 916 m_previousProgressTime = time; 917 m_sentStalledEvent = false; 918 if (renderer()) 919 renderer()->updateFromElement(); 920 } 921 } 922 923 void HTMLMediaElement::rewind(float timeDelta) 924 { 925 ExceptionCode e; 926 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e); 927 } 928 929 void HTMLMediaElement::returnToRealtime() 930 { 931 ExceptionCode e; 932 setCurrentTime(maxTimeSeekable(), e); 933 } 934 935 void HTMLMediaElement::addPlayedRange(float start, float end) 936 { 937 if (!m_playedTimeRanges) 938 m_playedTimeRanges = TimeRanges::create(); 939 m_playedTimeRanges->add(start, end); 940 } 941 942 bool HTMLMediaElement::supportsSave() const 943 { 944 return m_player ? m_player->supportsSave() : false; 945 } 946 947 void HTMLMediaElement::seek(float time, ExceptionCode& ec) 948 { 949 // 4.8.10.10. Seeking 950 // 1 951 if (m_readyState == HAVE_NOTHING || !m_player) { 952 ec = INVALID_STATE_ERR; 953 return; 954 } 955 956 // 2 957 time = min(time, duration()); 958 959 // 3 960 time = max(time, 0.0f); 961 962 // 4 963 RefPtr<TimeRanges> seekableRanges = seekable(); 964 if (!seekableRanges->contain(time)) { 965 ec = INDEX_SIZE_ERR; 966 return; 967 } 968 969 // avoid generating events when the time won't actually change 970 float now = currentTime(); 971 if (time == now) 972 return; 973 974 // 5 975 if (m_playing) { 976 if (m_lastSeekTime < now) 977 addPlayedRange(m_lastSeekTime, now); 978 } 979 m_lastSeekTime = time; 980 981 // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed 982 m_seeking = true; 983 984 // 7 985 scheduleTimeupdateEvent(false); 986 987 // 8 - this is covered, if necessary, when the engine signals a readystate change 988 989 // 10 990 m_player->seek(time); 991 m_sentEndEvent = false; 992 } 993 994 void HTMLMediaElement::finishSeek() 995 { 996 // 4.8.10.10 Seeking step 12 997 m_seeking = false; 998 999 // 4.8.10.10 Seeking step 13 1000 scheduleEvent(eventNames().seekedEvent); 1001 } 1002 1003 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const 1004 { 1005 return m_readyState; 1006 } 1007 1008 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const 1009 { 1010 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown; 1011 } 1012 1013 bool HTMLMediaElement::hasAudio() const 1014 { 1015 return m_player ? m_player->hasAudio() : false; 1016 } 1017 1018 bool HTMLMediaElement::seeking() const 1019 { 1020 return m_seeking; 1021 } 1022 1023 // playback state 1024 float HTMLMediaElement::currentTime() const 1025 { 1026 if (!m_player) 1027 return 0; 1028 if (m_seeking) 1029 return m_lastSeekTime; 1030 return m_player->currentTime(); 1031 } 1032 1033 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec) 1034 { 1035 seek(time, ec); 1036 } 1037 1038 float HTMLMediaElement::startTime() const 1039 { 1040 if (!m_player) 1041 return 0; 1042 return m_player->startTime(); 1043 } 1044 1045 float HTMLMediaElement::duration() const 1046 { 1047 if (m_readyState >= HAVE_METADATA) 1048 return m_player->duration(); 1049 1050 return numeric_limits<float>::quiet_NaN(); 1051 } 1052 1053 bool HTMLMediaElement::paused() const 1054 { 1055 return m_paused; 1056 } 1057 1058 float HTMLMediaElement::defaultPlaybackRate() const 1059 { 1060 return m_defaultPlaybackRate; 1061 } 1062 1063 void HTMLMediaElement::setDefaultPlaybackRate(float rate) 1064 { 1065 if (m_defaultPlaybackRate != rate) { 1066 m_defaultPlaybackRate = rate; 1067 scheduleEvent(eventNames().ratechangeEvent); 1068 } 1069 } 1070 1071 float HTMLMediaElement::playbackRate() const 1072 { 1073 return m_player ? m_player->rate() : 0; 1074 } 1075 1076 void HTMLMediaElement::setPlaybackRate(float rate) 1077 { 1078 if (m_playbackRate != rate) { 1079 m_playbackRate = rate; 1080 scheduleEvent(eventNames().ratechangeEvent); 1081 } 1082 if (m_player && potentiallyPlaying() && m_player->rate() != rate) 1083 m_player->setRate(rate); 1084 } 1085 1086 bool HTMLMediaElement::webkitPreservesPitch() const 1087 { 1088 return m_webkitPreservesPitch; 1089 } 1090 1091 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) 1092 { 1093 m_webkitPreservesPitch = preservesPitch; 1094 1095 if (!m_player) 1096 return; 1097 1098 m_player->setPreservesPitch(preservesPitch); 1099 } 1100 1101 bool HTMLMediaElement::ended() const 1102 { 1103 // 4.8.10.8 Playing the media resource 1104 // The ended attribute must return true if the media element has ended 1105 // playback and the direction of playback is forwards, and false otherwise. 1106 return endedPlayback() && m_playbackRate > 0; 1107 } 1108 1109 bool HTMLMediaElement::autoplay() const 1110 { 1111 return hasAttribute(autoplayAttr); 1112 } 1113 1114 void HTMLMediaElement::setAutoplay(bool b) 1115 { 1116 setBooleanAttribute(autoplayAttr, b); 1117 } 1118 1119 bool HTMLMediaElement::autobuffer() const 1120 { 1121 return hasAttribute(autobufferAttr); 1122 } 1123 1124 void HTMLMediaElement::setAutobuffer(bool b) 1125 { 1126 setBooleanAttribute(autobufferAttr, b); 1127 } 1128 1129 void HTMLMediaElement::play(bool isUserGesture) 1130 { 1131 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) 1132 return; 1133 1134 playInternal(); 1135 } 1136 1137 void HTMLMediaElement::playInternal() 1138 { 1139 // 4.8.10.9. Playing the media resource 1140 if (!m_player || m_networkState == NETWORK_EMPTY) 1141 scheduleLoad(); 1142 1143 if (endedPlayback()) { 1144 ExceptionCode unused; 1145 seek(0, unused); 1146 } 1147 1148 setPlaybackRate(defaultPlaybackRate()); 1149 1150 if (m_paused) { 1151 m_paused = false; 1152 scheduleEvent(eventNames().playEvent); 1153 1154 if (m_readyState <= HAVE_CURRENT_DATA) 1155 scheduleEvent(eventNames().waitingEvent); 1156 else if (m_readyState >= HAVE_FUTURE_DATA) 1157 scheduleEvent(eventNames().playingEvent); 1158 } 1159 m_autoplaying = false; 1160 1161 updatePlayState(); 1162 } 1163 1164 void HTMLMediaElement::pause(bool isUserGesture) 1165 { 1166 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) 1167 return; 1168 1169 pauseInternal(); 1170 } 1171 1172 1173 void HTMLMediaElement::pauseInternal() 1174 { 1175 // 4.8.10.9. Playing the media resource 1176 if (!m_player || m_networkState == NETWORK_EMPTY) 1177 scheduleLoad(); 1178 1179 m_autoplaying = false; 1180 1181 if (!m_paused) { 1182 m_paused = true; 1183 scheduleTimeupdateEvent(false); 1184 scheduleEvent(eventNames().pauseEvent); 1185 } 1186 1187 updatePlayState(); 1188 } 1189 1190 bool HTMLMediaElement::loop() const 1191 { 1192 return hasAttribute(loopAttr); 1193 } 1194 1195 void HTMLMediaElement::setLoop(bool b) 1196 { 1197 setBooleanAttribute(loopAttr, b); 1198 } 1199 1200 bool HTMLMediaElement::controls() const 1201 { 1202 Frame* frame = document()->frame(); 1203 1204 // always show controls when scripting is disabled 1205 if (frame && !frame->script()->canExecuteScripts()) 1206 return true; 1207 1208 return hasAttribute(controlsAttr); 1209 } 1210 1211 void HTMLMediaElement::setControls(bool b) 1212 { 1213 setBooleanAttribute(controlsAttr, b); 1214 } 1215 1216 float HTMLMediaElement::volume() const 1217 { 1218 return m_volume; 1219 } 1220 1221 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) 1222 { 1223 if (vol < 0.0f || vol > 1.0f) { 1224 ec = INDEX_SIZE_ERR; 1225 return; 1226 } 1227 1228 if (m_volume != vol) { 1229 m_volume = vol; 1230 updateVolume(); 1231 scheduleEvent(eventNames().volumechangeEvent); 1232 } 1233 } 1234 1235 bool HTMLMediaElement::muted() const 1236 { 1237 return m_muted; 1238 } 1239 1240 void HTMLMediaElement::setMuted(bool muted) 1241 { 1242 if (m_muted != muted) { 1243 m_muted = muted; 1244 // Avoid recursion when the player reports volume changes. 1245 if (!processingMediaPlayerCallback()) { 1246 if (m_player && m_player->supportsMuting()) { 1247 m_player->setMuted(m_muted); 1248 if (renderer()) 1249 renderer()->updateFromElement(); 1250 } else 1251 updateVolume(); 1252 } 1253 scheduleEvent(eventNames().volumechangeEvent); 1254 } 1255 } 1256 1257 void HTMLMediaElement::togglePlayState() 1258 { 1259 // We can safely call the internal play/pause methods, which don't check restrictions, because 1260 // this method is only called from the built-in media controller 1261 if (canPlay()) 1262 playInternal(); 1263 else 1264 pauseInternal(); 1265 } 1266 1267 void HTMLMediaElement::beginScrubbing() 1268 { 1269 if (!paused()) { 1270 if (ended()) { 1271 // Because a media element stays in non-paused state when it reaches end, playback resumes 1272 // when the slider is dragged from the end to another position unless we pause first. Do 1273 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. 1274 pause(processingUserGesture()); 1275 } else { 1276 // Not at the end but we still want to pause playback so the media engine doesn't try to 1277 // continue playing during scrubbing. Pause without generating an event as we will 1278 // unpause after scrubbing finishes. 1279 setPausedInternal(true); 1280 } 1281 } 1282 } 1283 1284 void HTMLMediaElement::endScrubbing() 1285 { 1286 if (m_pausedInternal) 1287 setPausedInternal(false); 1288 } 1289 1290 // The spec says to fire periodic timeupdate events (those sent while playing) every 1291 // "15 to 250ms", we choose the slowest frequency 1292 static const double maxTimeupdateEventFrequency = 0.25; 1293 1294 void HTMLMediaElement::startPlaybackProgressTimer() 1295 { 1296 if (m_playbackProgressTimer.isActive()) 1297 return; 1298 1299 m_previousProgressTime = WTF::currentTime(); 1300 m_previousProgress = 0; 1301 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); 1302 } 1303 1304 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) 1305 { 1306 ASSERT(m_player); 1307 if (!m_playbackRate) 1308 return; 1309 1310 scheduleTimeupdateEvent(true); 1311 1312 // FIXME: deal with cue ranges here 1313 } 1314 1315 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) 1316 { 1317 double now = WTF::currentTime(); 1318 double timedelta = now - m_lastTimeUpdateEventWallTime; 1319 1320 // throttle the periodic events 1321 if (periodicEvent && timedelta < maxTimeupdateEventFrequency) 1322 return; 1323 1324 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one 1325 // event at a given time so filter here 1326 float movieTime = m_player ? m_player->currentTime() : 0; 1327 if (movieTime != m_lastTimeUpdateEventMovieTime) { 1328 scheduleEvent(eventNames().timeupdateEvent); 1329 m_lastTimeUpdateEventWallTime = now; 1330 m_lastTimeUpdateEventMovieTime = movieTime; 1331 } 1332 } 1333 1334 bool HTMLMediaElement::canPlay() const 1335 { 1336 return paused() || ended() || m_readyState < HAVE_METADATA; 1337 } 1338 1339 float HTMLMediaElement::percentLoaded() const 1340 { 1341 if (!m_player) 1342 return 0; 1343 float duration = m_player->duration(); 1344 1345 if (!duration || isinf(duration)) 1346 return 0; 1347 1348 float buffered = 0; 1349 RefPtr<TimeRanges> timeRanges = m_player->buffered(); 1350 for (unsigned i = 0; i < timeRanges->length(); ++i) { 1351 ExceptionCode ignoredException; 1352 float start = timeRanges->start(i, ignoredException); 1353 float end = timeRanges->end(i, ignoredException); 1354 buffered += end - start; 1355 } 1356 return buffered / duration; 1357 } 1358 1359 bool HTMLMediaElement::havePotentialSourceChild() 1360 { 1361 // Stash the current <source> node so we can restore it after checking 1362 // to see there is another potential 1363 HTMLSourceElement* currentSourceNode = m_currentSourceNode; 1364 KURL nextURL = selectNextSourceChild(0, DoNothing); 1365 m_currentSourceNode = currentSourceNode; 1366 1367 return nextURL.isValid(); 1368 } 1369 1370 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid) 1371 { 1372 KURL mediaURL; 1373 Node* node; 1374 bool lookingForPreviousNode = m_currentSourceNode; 1375 bool canUse = false; 1376 1377 for (node = firstChild(); !canUse && node; node = node->nextSibling()) { 1378 if (!node->hasTagName(sourceTag)) 1379 continue; 1380 1381 if (lookingForPreviousNode) { 1382 if (m_currentSourceNode == static_cast<HTMLSourceElement*>(node)) 1383 lookingForPreviousNode = false; 1384 continue; 1385 } 1386 1387 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node); 1388 if (!source->hasAttribute(srcAttr)) 1389 goto check_again; 1390 1391 if (source->hasAttribute(mediaAttr)) { 1392 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); 1393 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); 1394 if (!screenEval.eval(media.get())) 1395 goto check_again; 1396 } 1397 1398 if (source->hasAttribute(typeAttr)) { 1399 if (!MediaPlayer::supportsType(ContentType(source->type()))) 1400 goto check_again; 1401 } 1402 1403 // Is it safe to load this url? 1404 mediaURL = source->src(); 1405 if (!mediaURL.isValid() || !isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string())) 1406 goto check_again; 1407 1408 // Making it this far means the <source> looks reasonable 1409 canUse = true; 1410 if (contentType) 1411 *contentType = ContentType(source->type()); 1412 1413 check_again: 1414 if (!canUse && actionIfInvalid == Complain) 1415 source->scheduleErrorEvent(); 1416 m_currentSourceNode = static_cast<HTMLSourceElement*>(node); 1417 } 1418 1419 if (!canUse) 1420 m_currentSourceNode = 0; 1421 return canUse ? mediaURL : KURL(); 1422 } 1423 1424 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) 1425 { 1426 beginProcessingMediaPlayerCallback(); 1427 1428 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, 1429 // it will only queue a 'timeupdate' event if we haven't already posted one at the current 1430 // movie time. 1431 scheduleTimeupdateEvent(false); 1432 1433 // 4.8.10.10 step 12 & 13. Needed if no ReadyState change is associated with the seek. 1434 if (m_readyState >= HAVE_CURRENT_DATA && m_seeking) 1435 finishSeek(); 1436 1437 float now = currentTime(); 1438 float dur = duration(); 1439 if (!isnan(dur) && dur && now >= dur) { 1440 if (loop()) { 1441 ExceptionCode ignoredException; 1442 m_sentEndEvent = false; 1443 seek(0, ignoredException); 1444 } else { 1445 if (!m_sentEndEvent) { 1446 m_sentEndEvent = true; 1447 scheduleEvent(eventNames().endedEvent); 1448 } 1449 } 1450 } 1451 else 1452 m_sentEndEvent = false; 1453 1454 updatePlayState(); 1455 endProcessingMediaPlayerCallback(); 1456 } 1457 1458 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) 1459 { 1460 beginProcessingMediaPlayerCallback(); 1461 if (m_player) 1462 m_volume = m_player->volume(); 1463 updateVolume(); 1464 endProcessingMediaPlayerCallback(); 1465 } 1466 1467 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) 1468 { 1469 beginProcessingMediaPlayerCallback(); 1470 if (m_player) 1471 setMuted(m_player->muted()); 1472 endProcessingMediaPlayerCallback(); 1473 } 1474 1475 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) 1476 { 1477 beginProcessingMediaPlayerCallback(); 1478 scheduleEvent(eventNames().durationchangeEvent); 1479 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1480 if (renderer()) { 1481 renderer()->updateFromElement(); 1482 if (renderer()->isVideo()) 1483 toRenderVideo(renderer())->videoSizeChanged(); 1484 } 1485 #endif 1486 endProcessingMediaPlayerCallback(); 1487 } 1488 1489 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) 1490 { 1491 beginProcessingMediaPlayerCallback(); 1492 // Stash the rate in case the one we tried to set isn't what the engine is 1493 // using (eg. it can't handle the rate we set) 1494 m_playbackRate = m_player->rate(); 1495 endProcessingMediaPlayerCallback(); 1496 } 1497 1498 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) 1499 { 1500 // The MediaPlayer came across content it cannot completely handle. 1501 // This is normally acceptable except when we are in a standalone 1502 // MediaDocument. If so, tell the document what has happened. 1503 if (ownerDocument()->isMediaDocument()) { 1504 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument()); 1505 mediaDocument->mediaElementSawUnsupportedTracks(); 1506 } 1507 } 1508 1509 // MediaPlayerPresentation methods 1510 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) 1511 { 1512 beginProcessingMediaPlayerCallback(); 1513 if (renderer()) 1514 renderer()->repaint(); 1515 endProcessingMediaPlayerCallback(); 1516 } 1517 1518 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) 1519 { 1520 beginProcessingMediaPlayerCallback(); 1521 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1522 if (renderer() && renderer()->isVideo()) 1523 toRenderVideo(renderer())->videoSizeChanged(); 1524 #endif 1525 endProcessingMediaPlayerCallback(); 1526 } 1527 1528 #if USE(ACCELERATED_COMPOSITING) 1529 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) 1530 { 1531 if (renderer() && renderer()->isVideo()) { 1532 ASSERT(renderer()->view()); 1533 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer())); 1534 } 1535 return false; 1536 } 1537 1538 GraphicsLayer* HTMLMediaElement::mediaPlayerGraphicsLayer(MediaPlayer*) 1539 { 1540 if (renderer() && renderer()->isVideo()) 1541 return toRenderVideo(renderer())->videoGraphicsLayer(); 1542 return 0; 1543 } 1544 #endif 1545 1546 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const 1547 { 1548 if (!m_player) 1549 return TimeRanges::create(); 1550 return m_player->buffered(); 1551 } 1552 1553 PassRefPtr<TimeRanges> HTMLMediaElement::played() 1554 { 1555 if (m_playing) { 1556 float time = currentTime(); 1557 if (time > m_lastSeekTime) 1558 addPlayedRange(m_lastSeekTime, time); 1559 } 1560 1561 if (!m_playedTimeRanges) 1562 m_playedTimeRanges = TimeRanges::create(); 1563 1564 return m_playedTimeRanges->copy(); 1565 } 1566 1567 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const 1568 { 1569 // FIXME real ranges support 1570 if (!maxTimeSeekable()) 1571 return TimeRanges::create(); 1572 return TimeRanges::create(minTimeSeekable(), maxTimeSeekable()); 1573 } 1574 1575 bool HTMLMediaElement::potentiallyPlaying() const 1576 { 1577 return m_readyState >= HAVE_FUTURE_DATA && couldPlayIfEnoughData(); 1578 } 1579 1580 bool HTMLMediaElement::couldPlayIfEnoughData() const 1581 { 1582 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); 1583 } 1584 1585 bool HTMLMediaElement::endedPlayback() const 1586 { 1587 float dur = duration(); 1588 if (!m_player || isnan(dur)) 1589 return false; 1590 1591 // 4.8.10.8 Playing the media resource 1592 1593 // A media element is said to have ended playback when the element's 1594 // readyState attribute is HAVE_METADATA or greater, 1595 if (m_readyState < HAVE_METADATA) 1596 return false; 1597 1598 // and the current playback position is the end of the media resource and the direction 1599 // of playback is forwards and the media element does not have a loop attribute specified, 1600 float now = currentTime(); 1601 if (m_playbackRate > 0) 1602 return now >= dur && !loop(); 1603 1604 // or the current playback position is the earliest possible position and the direction 1605 // of playback is backwards 1606 if (m_playbackRate < 0) 1607 return now <= 0; 1608 1609 return false; 1610 } 1611 1612 bool HTMLMediaElement::stoppedDueToErrors() const 1613 { 1614 if (m_readyState >= HAVE_METADATA && m_error) { 1615 RefPtr<TimeRanges> seekableRanges = seekable(); 1616 if (!seekableRanges->contain(currentTime())) 1617 return true; 1618 } 1619 1620 return false; 1621 } 1622 1623 bool HTMLMediaElement::pausedForUserInteraction() const 1624 { 1625 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] 1626 return false; 1627 } 1628 1629 float HTMLMediaElement::minTimeSeekable() const 1630 { 1631 return 0; 1632 } 1633 1634 float HTMLMediaElement::maxTimeSeekable() const 1635 { 1636 return m_player ? m_player->maxTimeSeekable() : 0; 1637 } 1638 1639 void HTMLMediaElement::updateVolume() 1640 { 1641 if (!m_player) 1642 return; 1643 1644 // Avoid recursion when the player reports volume changes. 1645 if (!processingMediaPlayerCallback()) { 1646 Page* page = document()->page(); 1647 float volumeMultiplier = page ? page->mediaVolume() : 1; 1648 1649 m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier); 1650 } 1651 1652 if (renderer()) 1653 renderer()->updateFromElement(); 1654 } 1655 1656 void HTMLMediaElement::updatePlayState() 1657 { 1658 if (!m_player) 1659 return; 1660 1661 if (m_pausedInternal) { 1662 if (!m_player->paused()) 1663 m_player->pause(); 1664 m_playbackProgressTimer.stop(); 1665 return; 1666 } 1667 1668 bool shouldBePlaying = potentiallyPlaying(); 1669 bool playerPaused = m_player->paused(); 1670 if (shouldBePlaying && playerPaused) { 1671 // Set rate before calling play in case the rate was set before the media engine wasn't setup. 1672 // The media engine should just stash the rate since it isn't already playing. 1673 m_player->setRate(m_playbackRate); 1674 m_player->play(); 1675 startPlaybackProgressTimer(); 1676 m_playing = true; 1677 } else if (!shouldBePlaying && !playerPaused) { 1678 m_player->pause(); 1679 m_playbackProgressTimer.stop(); 1680 m_playing = false; 1681 float time = currentTime(); 1682 if (time > m_lastSeekTime) 1683 addPlayedRange(m_lastSeekTime, time); 1684 } else if (couldPlayIfEnoughData() && playerPaused) 1685 m_player->prepareToPlay(); 1686 1687 if (renderer()) 1688 renderer()->updateFromElement(); 1689 } 1690 1691 void HTMLMediaElement::setPausedInternal(bool b) 1692 { 1693 m_pausedInternal = b; 1694 updatePlayState(); 1695 } 1696 1697 void HTMLMediaElement::stopPeriodicTimers() 1698 { 1699 m_progressEventTimer.stop(); 1700 m_playbackProgressTimer.stop(); 1701 } 1702 1703 void HTMLMediaElement::userCancelledLoad() 1704 { 1705 if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) 1706 return; 1707 1708 // If the media data fetching process is aborted by the user: 1709 1710 // 1 - The user agent should cancel the fetching process. 1711 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1712 m_player.clear(); 1713 #endif 1714 stopPeriodicTimers(); 1715 1716 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED. 1717 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); 1718 1719 // 3 - Queue a task to fire a progress event called abort at the media element, in the context 1720 // of the fetching process started by this instance of this algorithm. 1721 scheduleEvent(eventNames().abortEvent); 1722 1723 // 5 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the 1724 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a 1725 // simple event called emptied at the element. Otherwise, set set the element's networkState 1726 // attribute to the NETWORK_IDLE value. 1727 if (m_readyState == HAVE_NOTHING) { 1728 m_networkState = NETWORK_EMPTY; 1729 scheduleEvent(eventNames().emptiedEvent); 1730 } 1731 else 1732 m_networkState = NETWORK_IDLE; 1733 1734 // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 1735 m_delayingTheLoadEvent = false; 1736 1737 // 7 - Abort the overall resource selection algorithm. 1738 m_currentSourceNode = 0; 1739 } 1740 1741 void HTMLMediaElement::documentWillBecomeInactive() 1742 { 1743 if (m_isFullscreen) 1744 exitFullscreen(); 1745 1746 m_inActiveDocument = false; 1747 userCancelledLoad(); 1748 1749 // Stop the playback without generating events 1750 setPausedInternal(true); 1751 1752 if (renderer()) 1753 renderer()->updateFromElement(); 1754 1755 stopPeriodicTimers(); 1756 cancelPendingEventsAndCallbacks(); 1757 } 1758 1759 void HTMLMediaElement::documentDidBecomeActive() 1760 { 1761 m_inActiveDocument = true; 1762 setPausedInternal(false); 1763 1764 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { 1765 // Restart the load if it was aborted in the middle by moving the document to the page cache. 1766 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to 1767 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). 1768 // This behavior is not specified but it seems like a sensible thing to do. 1769 ExceptionCode ec; 1770 load(processingUserGesture(), ec); 1771 } 1772 1773 if (renderer()) 1774 renderer()->updateFromElement(); 1775 } 1776 1777 void HTMLMediaElement::mediaVolumeDidChange() 1778 { 1779 updateVolume(); 1780 } 1781 1782 const IntRect HTMLMediaElement::screenRect() 1783 { 1784 IntRect elementRect; 1785 if (renderer()) 1786 elementRect = renderer()->view()->frameView()->contentsToScreen(renderer()->absoluteBoundingBoxRect()); 1787 return elementRect; 1788 } 1789 1790 void HTMLMediaElement::defaultEventHandler(Event* event) 1791 { 1792 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1793 RenderObject* r = renderer(); 1794 if (!r || !r->isWidget()) 1795 return; 1796 1797 Widget* widget = toRenderWidget(r)->widget(); 1798 if (widget) 1799 widget->handleEvent(event); 1800 #else 1801 if (renderer() && renderer()->isMedia()) 1802 toRenderMedia(renderer())->forwardEvent(event); 1803 if (event->defaultHandled()) 1804 return; 1805 HTMLElement::defaultEventHandler(event); 1806 #endif 1807 } 1808 1809 bool HTMLMediaElement::processingUserGesture() const 1810 { 1811 Frame* frame = document()->frame(); 1812 FrameLoader* loader = frame ? frame->loader() : 0; 1813 1814 // return 'true' for safety if we don't know the answer 1815 return loader ? loader->isProcessingUserGesture() : true; 1816 } 1817 1818 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1819 1820 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) 1821 { 1822 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { 1823 togglePlayState(); 1824 return; 1825 } 1826 1827 if (m_player) 1828 m_player->deliverNotification(notification); 1829 } 1830 1831 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) 1832 { 1833 if (m_player) 1834 m_player->setMediaPlayerProxy(proxy); 1835 } 1836 1837 String HTMLMediaElement::initialURL() 1838 { 1839 KURL initialSrc = document()->completeURL(getAttribute(srcAttr)); 1840 1841 if (!initialSrc.isValid()) 1842 initialSrc = selectNextSourceChild(0, DoNothing); 1843 1844 m_currentSrc = initialSrc.string(); 1845 1846 return initialSrc; 1847 } 1848 1849 void HTMLMediaElement::finishParsingChildren() 1850 { 1851 HTMLElement::finishParsingChildren(); 1852 if (!m_player) 1853 m_player = MediaPlayer::create(this); 1854 1855 document()->updateStyleIfNeeded(); 1856 if (m_needWidgetUpdate && renderer()) 1857 toRenderEmbeddedObject(renderer())->updateWidget(true); 1858 } 1859 1860 #endif 1861 1862 void HTMLMediaElement::enterFullscreen() 1863 { 1864 ASSERT(!m_isFullscreen); 1865 if (document() && document()->page()) { 1866 document()->page()->chrome()->client()->enterFullscreenForNode(this); 1867 scheduleEvent(eventNames().webkitbeginfullscreenEvent); 1868 m_isFullscreen = true; 1869 } 1870 } 1871 1872 void HTMLMediaElement::exitFullscreen() 1873 { 1874 ASSERT(m_isFullscreen); 1875 if (document() && document()->page()) { 1876 document()->page()->chrome()->client()->exitFullscreenForNode(this); 1877 scheduleEvent(eventNames().webkitendfullscreenEvent); 1878 } 1879 m_isFullscreen = false; 1880 } 1881 1882 PlatformMedia HTMLMediaElement::platformMedia() const 1883 { 1884 return m_player ? m_player->platformMedia() : NoPlatformMedia; 1885 } 1886 1887 bool HTMLMediaElement::hasClosedCaptions() const 1888 { 1889 return m_player && m_player->hasClosedCaptions(); 1890 } 1891 1892 bool HTMLMediaElement::closedCaptionsVisible() const 1893 { 1894 return m_closedCaptionsVisible; 1895 } 1896 1897 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) 1898 { 1899 if (!m_player ||!hasClosedCaptions()) 1900 return; 1901 1902 m_closedCaptionsVisible = closedCaptionVisible; 1903 m_player->setClosedCaptionsVisible(closedCaptionVisible); 1904 if (renderer()) 1905 renderer()->updateFromElement(); 1906 } 1907 1908 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible) 1909 { 1910 setClosedCaptionsVisible(visible); 1911 } 1912 1913 bool HTMLMediaElement::webkitClosedCaptionsVisible() const 1914 { 1915 return closedCaptionsVisible(); 1916 } 1917 1918 1919 bool HTMLMediaElement::webkitHasClosedCaptions() const 1920 { 1921 return hasClosedCaptions(); 1922 } 1923 1924 } 1925 1926 #endif 1927