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 m_player->load(m_currentSrc, contentType); 594 595 if (isVideo() && m_player->canLoadPoster()) { 596 KURL posterUrl = static_cast<HTMLVideoElement*>(this)->poster(); 597 if (!posterUrl.isEmpty()) 598 m_player->setPoster(posterUrl); 599 } 600 601 if (renderer()) 602 renderer()->updateFromElement(); 603 } 604 605 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid) 606 { 607 Frame* frame = document()->frame(); 608 FrameLoader* loader = frame ? frame->loader() : 0; 609 610 // don't allow remote to local urls, and check with the frame loader client. 611 if (!loader || !SecurityOrigin::canLoad(url, String(), document())) { 612 if (actionIfInvalid == Complain) 613 FrameLoader::reportLocalLoadFailed(frame, url.string()); 614 return false; 615 } 616 617 return true; 618 } 619 620 void HTMLMediaElement::startProgressEventTimer() 621 { 622 if (m_progressEventTimer.isActive()) 623 return; 624 625 m_previousProgressTime = WTF::currentTime(); 626 m_previousProgress = 0; 627 // 350ms is not magic, it is in the spec! 628 m_progressEventTimer.startRepeating(0.350); 629 } 630 631 void HTMLMediaElement::waitForSourceChange() 632 { 633 stopPeriodicTimers(); 634 m_loadState = WaitingForSource; 635 636 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value 637 m_networkState = NETWORK_NO_SOURCE; 638 639 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 640 m_delayingTheLoadEvent = false; 641 } 642 643 void HTMLMediaElement::noneSupported() 644 { 645 stopPeriodicTimers(); 646 m_loadState = WaitingForSource; 647 m_currentSourceNode = 0; 648 649 // 5 - Reaching this step indicates that either the URL failed to resolve, or the media 650 // resource failed to load. Set the error attribute to a new MediaError object whose 651 // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED. 652 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); 653 654 // 6 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value. 655 m_networkState = NETWORK_NO_SOURCE; 656 657 // 7 - Queue a task to fire a progress event called error at the media element, in 658 // the context of the fetching process that was used to try to obtain the media 659 // resource in the resource fetch algorithm. 660 scheduleEvent(eventNames().errorEvent); 661 662 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 663 m_delayingTheLoadEvent = false; 664 665 // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource. 666 667 if (isVideo()) 668 static_cast<HTMLVideoElement*>(this)->updatePosterImage(); 669 if (renderer()) 670 renderer()->updateFromElement(); 671 } 672 673 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) 674 { 675 // 1 - The user agent should cancel the fetching process. 676 stopPeriodicTimers(); 677 m_loadState = WaitingForSource; 678 679 // 2 - Set the error attribute to a new MediaError object whose code attribute is 680 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE. 681 m_error = err; 682 683 // 3 - Queue a task to fire a progress event called error at the media element, in 684 // the context of the fetching process started by this instance of this algorithm. 685 scheduleEvent(eventNames().errorEvent); 686 687 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a 688 // task to fire a simple event called emptied at the element. 689 m_networkState = NETWORK_EMPTY; 690 scheduleEvent(eventNames().emptiedEvent); 691 692 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 693 m_delayingTheLoadEvent = false; 694 695 // 6 - Abort the overall resource selection algorithm. 696 m_currentSourceNode = 0; 697 } 698 699 void HTMLMediaElement::cancelPendingEventsAndCallbacks() 700 { 701 m_pendingEvents.clear(); 702 703 for (Node* node = firstChild(); node; node = node->nextSibling()) { 704 if (node->hasTagName(sourceTag)) 705 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent(); 706 } 707 } 708 709 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) 710 { 711 beginProcessingMediaPlayerCallback(); 712 setNetworkState(m_player->networkState()); 713 endProcessingMediaPlayerCallback(); 714 } 715 716 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) 717 { 718 if (state == MediaPlayer::Empty) { 719 // just update the cached state and leave, we can't do anything 720 m_networkState = NETWORK_EMPTY; 721 return; 722 } 723 724 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) { 725 stopPeriodicTimers(); 726 727 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more 728 // <source> children, schedule the next one 729 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) { 730 m_currentSourceNode->scheduleErrorEvent(); 731 if (havePotentialSourceChild()) 732 scheduleNextSourceChild(); 733 else 734 waitForSourceChange(); 735 736 return; 737 } 738 739 if (state == MediaPlayer::NetworkError) 740 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK)); 741 else if (state == MediaPlayer::DecodeError) 742 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE)); 743 else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr) 744 noneSupported(); 745 746 if (isVideo()) 747 static_cast<HTMLVideoElement*>(this)->updatePosterImage(); 748 749 return; 750 } 751 752 if (state == MediaPlayer::Idle) { 753 if (m_networkState > NETWORK_IDLE) { 754 stopPeriodicTimers(); 755 scheduleEvent(eventNames().suspendEvent); 756 } 757 m_networkState = NETWORK_IDLE; 758 } 759 760 if (state == MediaPlayer::Loading) { 761 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE) 762 startProgressEventTimer(); 763 m_networkState = NETWORK_LOADING; 764 } 765 766 if (state == MediaPlayer::Loaded) { 767 NetworkState oldState = m_networkState; 768 769 m_networkState = NETWORK_LOADED; 770 if (oldState < NETWORK_LOADED || oldState == NETWORK_NO_SOURCE) { 771 m_progressEventTimer.stop(); 772 773 // Schedule one last progress event so we guarantee that at least one is fired 774 // for files that load very quickly. 775 scheduleEvent(eventNames().progressEvent); 776 777 // Check to see if readyState changes need to be dealt with before sending the 778 // 'load' event so we report 'canplaythrough' first. This is necessary because a 779 // media engine reports readyState and networkState changes separately 780 MediaPlayer::ReadyState currentState = m_player->readyState(); 781 if (static_cast<ReadyState>(currentState) != m_readyState) 782 setReadyState(currentState); 783 784 scheduleEvent(eventNames().loadEvent); 785 } 786 } 787 } 788 789 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) 790 { 791 beginProcessingMediaPlayerCallback(); 792 793 setReadyState(m_player->readyState()); 794 795 endProcessingMediaPlayerCallback(); 796 } 797 798 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) 799 { 800 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it 801 bool wasPotentiallyPlaying = potentiallyPlaying(); 802 803 ReadyState oldState = m_readyState; 804 m_readyState = static_cast<ReadyState>(state); 805 806 if (m_readyState == oldState) 807 return; 808 809 if (m_networkState == NETWORK_EMPTY) 810 return; 811 812 if (m_seeking) { 813 // 4.8.10.10, step 8 814 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) 815 scheduleEvent(eventNames().waitingEvent); 816 817 // 4.8.10.10, step 9 818 if (m_readyState < HAVE_CURRENT_DATA) { 819 if (oldState >= HAVE_CURRENT_DATA) 820 scheduleEvent(eventNames().seekingEvent); 821 } else { 822 // 4.8.10.10 step 12 & 13. 823 finishSeek(); 824 } 825 826 } else { 827 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) { 828 // 4.8.10.9 829 scheduleTimeupdateEvent(false); 830 scheduleEvent(eventNames().waitingEvent); 831 } 832 } 833 834 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) { 835 scheduleEvent(eventNames().durationchangeEvent); 836 scheduleEvent(eventNames().loadedmetadataEvent); 837 838 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 839 if (renderer() && renderer()->isVideo()) { 840 toRenderVideo(renderer())->videoSizeChanged(); 841 } 842 #endif 843 m_delayingTheLoadEvent = false; 844 m_player->seek(0); 845 } 846 847 bool shouldUpdatePosterImage = false; 848 849 // 4.8.10.7 says loadeddata is sent only when the new state *is* HAVE_CURRENT_DATA: "If the 850 // previous ready state was HAVE_METADATA and the new ready state is HAVE_CURRENT_DATA", 851 // but the event table at the end of the spec says it is sent when: "readyState newly 852 // increased to HAVE_CURRENT_DATA or greater for the first time" 853 // We go with the later because it seems useful to count on getting this event 854 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) { 855 m_haveFiredLoadedData = true; 856 shouldUpdatePosterImage = true; 857 scheduleEvent(eventNames().loadeddataEvent); 858 } 859 860 bool isPotentiallyPlaying = potentiallyPlaying(); 861 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) { 862 scheduleEvent(eventNames().canplayEvent); 863 if (isPotentiallyPlaying) 864 scheduleEvent(eventNames().playingEvent); 865 shouldUpdatePosterImage = true; 866 } 867 868 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) { 869 if (oldState <= HAVE_CURRENT_DATA) 870 scheduleEvent(eventNames().canplayEvent); 871 872 scheduleEvent(eventNames().canplaythroughEvent); 873 874 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) 875 scheduleEvent(eventNames().playingEvent); 876 877 if (m_autoplaying && m_paused && autoplay()) { 878 m_paused = false; 879 scheduleEvent(eventNames().playEvent); 880 scheduleEvent(eventNames().playingEvent); 881 } 882 883 shouldUpdatePosterImage = true; 884 } 885 886 if (shouldUpdatePosterImage && isVideo()) 887 static_cast<HTMLVideoElement*>(this)->updatePosterImage(); 888 889 updatePlayState(); 890 } 891 892 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) 893 { 894 ASSERT(m_player); 895 if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) 896 return; 897 898 unsigned progress = m_player->bytesLoaded(); 899 double time = WTF::currentTime(); 900 double timedelta = time - m_previousProgressTime; 901 902 if (progress == m_previousProgress) { 903 if (timedelta > 3.0 && !m_sentStalledEvent) { 904 scheduleEvent(eventNames().stalledEvent); 905 m_sentStalledEvent = true; 906 } 907 } else { 908 scheduleEvent(eventNames().progressEvent); 909 m_previousProgress = progress; 910 m_previousProgressTime = time; 911 m_sentStalledEvent = false; 912 if (renderer()) 913 renderer()->updateFromElement(); 914 } 915 } 916 917 void HTMLMediaElement::rewind(float timeDelta) 918 { 919 ExceptionCode e; 920 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e); 921 } 922 923 void HTMLMediaElement::returnToRealtime() 924 { 925 ExceptionCode e; 926 setCurrentTime(maxTimeSeekable(), e); 927 } 928 929 void HTMLMediaElement::addPlayedRange(float start, float end) 930 { 931 if (!m_playedTimeRanges) 932 m_playedTimeRanges = TimeRanges::create(); 933 m_playedTimeRanges->add(start, end); 934 } 935 936 bool HTMLMediaElement::supportsSave() const 937 { 938 return m_player ? m_player->supportsSave() : false; 939 } 940 941 void HTMLMediaElement::seek(float time, ExceptionCode& ec) 942 { 943 // 4.8.10.10. Seeking 944 // 1 945 if (m_readyState == HAVE_NOTHING || !m_player) { 946 ec = INVALID_STATE_ERR; 947 return; 948 } 949 950 // 2 951 time = min(time, duration()); 952 953 // 3 954 time = max(time, 0.0f); 955 956 // 4 957 RefPtr<TimeRanges> seekableRanges = seekable(); 958 if (!seekableRanges->contain(time)) { 959 ec = INDEX_SIZE_ERR; 960 return; 961 } 962 963 // avoid generating events when the time won't actually change 964 float now = currentTime(); 965 if (time == now) 966 return; 967 968 // 5 969 if (m_playing) { 970 if (m_lastSeekTime < now) 971 addPlayedRange(m_lastSeekTime, now); 972 } 973 m_lastSeekTime = time; 974 975 // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed 976 m_seeking = true; 977 978 // 7 979 scheduleTimeupdateEvent(false); 980 981 // 8 - this is covered, if necessary, when the engine signals a readystate change 982 983 // 10 984 m_player->seek(time); 985 m_sentEndEvent = false; 986 } 987 988 void HTMLMediaElement::finishSeek() 989 { 990 // 4.8.10.10 Seeking step 12 991 m_seeking = false; 992 993 // 4.8.10.10 Seeking step 13 994 scheduleEvent(eventNames().seekedEvent); 995 } 996 997 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const 998 { 999 return m_readyState; 1000 } 1001 1002 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const 1003 { 1004 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown; 1005 } 1006 1007 bool HTMLMediaElement::hasAudio() const 1008 { 1009 return m_player ? m_player->hasAudio() : false; 1010 } 1011 1012 bool HTMLMediaElement::seeking() const 1013 { 1014 return m_seeking; 1015 } 1016 1017 // playback state 1018 float HTMLMediaElement::currentTime() const 1019 { 1020 if (!m_player) 1021 return 0; 1022 if (m_seeking) 1023 return m_lastSeekTime; 1024 return m_player->currentTime(); 1025 } 1026 1027 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec) 1028 { 1029 seek(time, ec); 1030 } 1031 1032 float HTMLMediaElement::startTime() const 1033 { 1034 if (!m_player) 1035 return 0; 1036 return m_player->startTime(); 1037 } 1038 1039 float HTMLMediaElement::duration() const 1040 { 1041 if (m_readyState >= HAVE_METADATA) 1042 return m_player->duration(); 1043 1044 return numeric_limits<float>::quiet_NaN(); 1045 } 1046 1047 bool HTMLMediaElement::paused() const 1048 { 1049 return m_paused; 1050 } 1051 1052 float HTMLMediaElement::defaultPlaybackRate() const 1053 { 1054 return m_defaultPlaybackRate; 1055 } 1056 1057 void HTMLMediaElement::setDefaultPlaybackRate(float rate) 1058 { 1059 if (m_defaultPlaybackRate != rate) { 1060 m_defaultPlaybackRate = rate; 1061 scheduleEvent(eventNames().ratechangeEvent); 1062 } 1063 } 1064 1065 float HTMLMediaElement::playbackRate() const 1066 { 1067 return m_player ? m_player->rate() : 0; 1068 } 1069 1070 void HTMLMediaElement::setPlaybackRate(float rate) 1071 { 1072 if (m_playbackRate != rate) { 1073 m_playbackRate = rate; 1074 scheduleEvent(eventNames().ratechangeEvent); 1075 } 1076 if (m_player && potentiallyPlaying() && m_player->rate() != rate) 1077 m_player->setRate(rate); 1078 } 1079 1080 bool HTMLMediaElement::webkitPreservesPitch() const 1081 { 1082 return m_webkitPreservesPitch; 1083 } 1084 1085 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) 1086 { 1087 m_webkitPreservesPitch = preservesPitch; 1088 1089 if (!m_player) 1090 return; 1091 1092 m_player->setPreservesPitch(preservesPitch); 1093 } 1094 1095 bool HTMLMediaElement::ended() const 1096 { 1097 // 4.8.10.8 Playing the media resource 1098 // The ended attribute must return true if the media element has ended 1099 // playback and the direction of playback is forwards, and false otherwise. 1100 return endedPlayback() && m_playbackRate > 0; 1101 } 1102 1103 bool HTMLMediaElement::autoplay() const 1104 { 1105 return hasAttribute(autoplayAttr); 1106 } 1107 1108 void HTMLMediaElement::setAutoplay(bool b) 1109 { 1110 setBooleanAttribute(autoplayAttr, b); 1111 } 1112 1113 bool HTMLMediaElement::autobuffer() const 1114 { 1115 return hasAttribute(autobufferAttr); 1116 } 1117 1118 void HTMLMediaElement::setAutobuffer(bool b) 1119 { 1120 setBooleanAttribute(autobufferAttr, b); 1121 } 1122 1123 void HTMLMediaElement::play(bool isUserGesture) 1124 { 1125 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) 1126 return; 1127 1128 playInternal(); 1129 } 1130 1131 void HTMLMediaElement::playInternal() 1132 { 1133 // 4.8.10.9. Playing the media resource 1134 if (!m_player || m_networkState == NETWORK_EMPTY) 1135 scheduleLoad(); 1136 1137 if (endedPlayback()) { 1138 ExceptionCode unused; 1139 seek(0, unused); 1140 } 1141 1142 setPlaybackRate(defaultPlaybackRate()); 1143 1144 if (m_paused) { 1145 m_paused = false; 1146 scheduleEvent(eventNames().playEvent); 1147 1148 if (m_readyState <= HAVE_CURRENT_DATA) 1149 scheduleEvent(eventNames().waitingEvent); 1150 else if (m_readyState >= HAVE_FUTURE_DATA) 1151 scheduleEvent(eventNames().playingEvent); 1152 } 1153 m_autoplaying = false; 1154 1155 updatePlayState(); 1156 } 1157 1158 void HTMLMediaElement::pause(bool isUserGesture) 1159 { 1160 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) 1161 return; 1162 1163 pauseInternal(); 1164 } 1165 1166 1167 void HTMLMediaElement::pauseInternal() 1168 { 1169 // 4.8.10.9. Playing the media resource 1170 if (!m_player || m_networkState == NETWORK_EMPTY) 1171 scheduleLoad(); 1172 1173 m_autoplaying = false; 1174 1175 if (!m_paused) { 1176 m_paused = true; 1177 scheduleTimeupdateEvent(false); 1178 scheduleEvent(eventNames().pauseEvent); 1179 } 1180 1181 updatePlayState(); 1182 } 1183 1184 bool HTMLMediaElement::loop() const 1185 { 1186 return hasAttribute(loopAttr); 1187 } 1188 1189 void HTMLMediaElement::setLoop(bool b) 1190 { 1191 setBooleanAttribute(loopAttr, b); 1192 } 1193 1194 bool HTMLMediaElement::controls() const 1195 { 1196 Frame* frame = document()->frame(); 1197 1198 // always show controls when scripting is disabled 1199 if (frame && !frame->script()->canExecuteScripts()) 1200 return true; 1201 1202 return hasAttribute(controlsAttr); 1203 } 1204 1205 void HTMLMediaElement::setControls(bool b) 1206 { 1207 setBooleanAttribute(controlsAttr, b); 1208 } 1209 1210 float HTMLMediaElement::volume() const 1211 { 1212 return m_volume; 1213 } 1214 1215 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) 1216 { 1217 if (vol < 0.0f || vol > 1.0f) { 1218 ec = INDEX_SIZE_ERR; 1219 return; 1220 } 1221 1222 if (m_volume != vol) { 1223 m_volume = vol; 1224 updateVolume(); 1225 scheduleEvent(eventNames().volumechangeEvent); 1226 } 1227 } 1228 1229 bool HTMLMediaElement::muted() const 1230 { 1231 return m_muted; 1232 } 1233 1234 void HTMLMediaElement::setMuted(bool muted) 1235 { 1236 if (m_muted != muted) { 1237 m_muted = muted; 1238 // Avoid recursion when the player reports volume changes. 1239 if (!processingMediaPlayerCallback()) { 1240 if (m_player && m_player->supportsMuting()) { 1241 m_player->setMuted(m_muted); 1242 if (renderer()) 1243 renderer()->updateFromElement(); 1244 } else 1245 updateVolume(); 1246 } 1247 scheduleEvent(eventNames().volumechangeEvent); 1248 } 1249 } 1250 1251 void HTMLMediaElement::togglePlayState() 1252 { 1253 // We can safely call the internal play/pause methods, which don't check restrictions, because 1254 // this method is only called from the built-in media controller 1255 if (canPlay()) 1256 playInternal(); 1257 else 1258 pauseInternal(); 1259 } 1260 1261 void HTMLMediaElement::beginScrubbing() 1262 { 1263 if (!paused()) { 1264 if (ended()) { 1265 // Because a media element stays in non-paused state when it reaches end, playback resumes 1266 // when the slider is dragged from the end to another position unless we pause first. Do 1267 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. 1268 pause(processingUserGesture()); 1269 } else { 1270 // Not at the end but we still want to pause playback so the media engine doesn't try to 1271 // continue playing during scrubbing. Pause without generating an event as we will 1272 // unpause after scrubbing finishes. 1273 setPausedInternal(true); 1274 } 1275 } 1276 } 1277 1278 void HTMLMediaElement::endScrubbing() 1279 { 1280 if (m_pausedInternal) 1281 setPausedInternal(false); 1282 } 1283 1284 // The spec says to fire periodic timeupdate events (those sent while playing) every 1285 // "15 to 250ms", we choose the slowest frequency 1286 static const double maxTimeupdateEventFrequency = 0.25; 1287 1288 void HTMLMediaElement::startPlaybackProgressTimer() 1289 { 1290 if (m_playbackProgressTimer.isActive()) 1291 return; 1292 1293 m_previousProgressTime = WTF::currentTime(); 1294 m_previousProgress = 0; 1295 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); 1296 } 1297 1298 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) 1299 { 1300 ASSERT(m_player); 1301 if (!m_playbackRate) 1302 return; 1303 1304 scheduleTimeupdateEvent(true); 1305 1306 // FIXME: deal with cue ranges here 1307 } 1308 1309 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) 1310 { 1311 double now = WTF::currentTime(); 1312 double timedelta = now - m_lastTimeUpdateEventWallTime; 1313 1314 // throttle the periodic events 1315 if (periodicEvent && timedelta < maxTimeupdateEventFrequency) 1316 return; 1317 1318 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one 1319 // event at a given time so filter here 1320 float movieTime = m_player ? m_player->currentTime() : 0; 1321 if (movieTime != m_lastTimeUpdateEventMovieTime) { 1322 scheduleEvent(eventNames().timeupdateEvent); 1323 m_lastTimeUpdateEventWallTime = now; 1324 m_lastTimeUpdateEventMovieTime = movieTime; 1325 } 1326 } 1327 1328 bool HTMLMediaElement::canPlay() const 1329 { 1330 return paused() || ended() || m_readyState < HAVE_METADATA; 1331 } 1332 1333 float HTMLMediaElement::percentLoaded() const 1334 { 1335 if (!m_player) 1336 return 0; 1337 float duration = m_player->duration(); 1338 1339 if (!duration || isinf(duration)) 1340 return 0; 1341 1342 float buffered = 0; 1343 RefPtr<TimeRanges> timeRanges = m_player->buffered(); 1344 for (unsigned i = 0; i < timeRanges->length(); ++i) { 1345 ExceptionCode ignoredException; 1346 float start = timeRanges->start(i, ignoredException); 1347 float end = timeRanges->end(i, ignoredException); 1348 buffered += end - start; 1349 } 1350 return buffered / duration; 1351 } 1352 1353 bool HTMLMediaElement::havePotentialSourceChild() 1354 { 1355 // Stash the current <source> node so we can restore it after checking 1356 // to see there is another potential 1357 HTMLSourceElement* currentSourceNode = m_currentSourceNode; 1358 KURL nextURL = selectNextSourceChild(0, DoNothing); 1359 m_currentSourceNode = currentSourceNode; 1360 1361 return nextURL.isValid(); 1362 } 1363 1364 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid) 1365 { 1366 KURL mediaURL; 1367 Node* node; 1368 bool lookingForPreviousNode = m_currentSourceNode; 1369 bool canUse = false; 1370 1371 for (node = firstChild(); !canUse && node; node = node->nextSibling()) { 1372 if (!node->hasTagName(sourceTag)) 1373 continue; 1374 1375 if (lookingForPreviousNode) { 1376 if (m_currentSourceNode == static_cast<HTMLSourceElement*>(node)) 1377 lookingForPreviousNode = false; 1378 continue; 1379 } 1380 1381 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node); 1382 if (!source->hasAttribute(srcAttr)) 1383 goto check_again; 1384 1385 if (source->hasAttribute(mediaAttr)) { 1386 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); 1387 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); 1388 if (!screenEval.eval(media.get())) 1389 goto check_again; 1390 } 1391 1392 if (source->hasAttribute(typeAttr)) { 1393 if (!MediaPlayer::supportsType(ContentType(source->type()))) 1394 goto check_again; 1395 } 1396 1397 // Is it safe to load this url? 1398 mediaURL = source->src(); 1399 if (!mediaURL.isValid() || !isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string())) 1400 goto check_again; 1401 1402 // Making it this far means the <source> looks reasonable 1403 canUse = true; 1404 if (contentType) 1405 *contentType = ContentType(source->type()); 1406 1407 check_again: 1408 if (!canUse && actionIfInvalid == Complain) 1409 source->scheduleErrorEvent(); 1410 m_currentSourceNode = static_cast<HTMLSourceElement*>(node); 1411 } 1412 1413 if (!canUse) 1414 m_currentSourceNode = 0; 1415 return canUse ? mediaURL : KURL(); 1416 } 1417 1418 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) 1419 { 1420 beginProcessingMediaPlayerCallback(); 1421 1422 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, 1423 // it will only queue a 'timeupdate' event if we haven't already posted one at the current 1424 // movie time. 1425 scheduleTimeupdateEvent(false); 1426 1427 // 4.8.10.10 step 12 & 13. Needed if no ReadyState change is associated with the seek. 1428 if (m_readyState >= HAVE_CURRENT_DATA && m_seeking) 1429 finishSeek(); 1430 1431 float now = currentTime(); 1432 float dur = duration(); 1433 if (!isnan(dur) && dur && now >= dur) { 1434 if (loop()) { 1435 ExceptionCode ignoredException; 1436 m_sentEndEvent = false; 1437 seek(0, ignoredException); 1438 } else { 1439 if (!m_sentEndEvent) { 1440 m_sentEndEvent = true; 1441 scheduleEvent(eventNames().endedEvent); 1442 } 1443 } 1444 } 1445 else 1446 m_sentEndEvent = false; 1447 1448 updatePlayState(); 1449 endProcessingMediaPlayerCallback(); 1450 } 1451 1452 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) 1453 { 1454 beginProcessingMediaPlayerCallback(); 1455 if (m_player) 1456 m_volume = m_player->volume(); 1457 updateVolume(); 1458 endProcessingMediaPlayerCallback(); 1459 } 1460 1461 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) 1462 { 1463 beginProcessingMediaPlayerCallback(); 1464 if (m_player) 1465 setMuted(m_player->muted()); 1466 endProcessingMediaPlayerCallback(); 1467 } 1468 1469 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) 1470 { 1471 beginProcessingMediaPlayerCallback(); 1472 scheduleEvent(eventNames().durationchangeEvent); 1473 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1474 if (renderer()) { 1475 renderer()->updateFromElement(); 1476 if (renderer()->isVideo()) 1477 toRenderVideo(renderer())->videoSizeChanged(); 1478 } 1479 #endif 1480 endProcessingMediaPlayerCallback(); 1481 } 1482 1483 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) 1484 { 1485 beginProcessingMediaPlayerCallback(); 1486 // Stash the rate in case the one we tried to set isn't what the engine is 1487 // using (eg. it can't handle the rate we set) 1488 m_playbackRate = m_player->rate(); 1489 endProcessingMediaPlayerCallback(); 1490 } 1491 1492 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) 1493 { 1494 // The MediaPlayer came across content it cannot completely handle. 1495 // This is normally acceptable except when we are in a standalone 1496 // MediaDocument. If so, tell the document what has happened. 1497 if (ownerDocument()->isMediaDocument()) { 1498 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument()); 1499 mediaDocument->mediaElementSawUnsupportedTracks(); 1500 } 1501 } 1502 1503 // MediaPlayerPresentation methods 1504 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) 1505 { 1506 beginProcessingMediaPlayerCallback(); 1507 if (renderer()) 1508 renderer()->repaint(); 1509 endProcessingMediaPlayerCallback(); 1510 } 1511 1512 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) 1513 { 1514 beginProcessingMediaPlayerCallback(); 1515 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1516 if (renderer() && renderer()->isVideo()) 1517 toRenderVideo(renderer())->videoSizeChanged(); 1518 #endif 1519 endProcessingMediaPlayerCallback(); 1520 } 1521 1522 #if USE(ACCELERATED_COMPOSITING) 1523 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) 1524 { 1525 if (renderer() && renderer()->isVideo()) { 1526 ASSERT(renderer()->view()); 1527 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer())); 1528 } 1529 return false; 1530 } 1531 1532 GraphicsLayer* HTMLMediaElement::mediaPlayerGraphicsLayer(MediaPlayer*) 1533 { 1534 if (renderer() && renderer()->isVideo()) 1535 return toRenderVideo(renderer())->videoGraphicsLayer(); 1536 return 0; 1537 } 1538 #endif 1539 1540 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const 1541 { 1542 if (!m_player) 1543 return TimeRanges::create(); 1544 return m_player->buffered(); 1545 } 1546 1547 PassRefPtr<TimeRanges> HTMLMediaElement::played() 1548 { 1549 if (m_playing) { 1550 float time = currentTime(); 1551 if (time > m_lastSeekTime) 1552 addPlayedRange(m_lastSeekTime, time); 1553 } 1554 1555 if (!m_playedTimeRanges) 1556 m_playedTimeRanges = TimeRanges::create(); 1557 1558 return m_playedTimeRanges->copy(); 1559 } 1560 1561 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const 1562 { 1563 // FIXME real ranges support 1564 if (!maxTimeSeekable()) 1565 return TimeRanges::create(); 1566 return TimeRanges::create(minTimeSeekable(), maxTimeSeekable()); 1567 } 1568 1569 bool HTMLMediaElement::potentiallyPlaying() const 1570 { 1571 return m_readyState >= HAVE_FUTURE_DATA && couldPlayIfEnoughData(); 1572 } 1573 1574 bool HTMLMediaElement::couldPlayIfEnoughData() const 1575 { 1576 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); 1577 } 1578 1579 bool HTMLMediaElement::endedPlayback() const 1580 { 1581 float dur = duration(); 1582 if (!m_player || isnan(dur)) 1583 return false; 1584 1585 // 4.8.10.8 Playing the media resource 1586 1587 // A media element is said to have ended playback when the element's 1588 // readyState attribute is HAVE_METADATA or greater, 1589 if (m_readyState < HAVE_METADATA) 1590 return false; 1591 1592 // and the current playback position is the end of the media resource and the direction 1593 // of playback is forwards and the media element does not have a loop attribute specified, 1594 float now = currentTime(); 1595 if (m_playbackRate > 0) 1596 return now >= dur && !loop(); 1597 1598 // or the current playback position is the earliest possible position and the direction 1599 // of playback is backwards 1600 if (m_playbackRate < 0) 1601 return now <= 0; 1602 1603 return false; 1604 } 1605 1606 bool HTMLMediaElement::stoppedDueToErrors() const 1607 { 1608 if (m_readyState >= HAVE_METADATA && m_error) { 1609 RefPtr<TimeRanges> seekableRanges = seekable(); 1610 if (!seekableRanges->contain(currentTime())) 1611 return true; 1612 } 1613 1614 return false; 1615 } 1616 1617 bool HTMLMediaElement::pausedForUserInteraction() const 1618 { 1619 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] 1620 return false; 1621 } 1622 1623 float HTMLMediaElement::minTimeSeekable() const 1624 { 1625 return 0; 1626 } 1627 1628 float HTMLMediaElement::maxTimeSeekable() const 1629 { 1630 return m_player ? m_player->maxTimeSeekable() : 0; 1631 } 1632 1633 void HTMLMediaElement::updateVolume() 1634 { 1635 if (!m_player) 1636 return; 1637 1638 // Avoid recursion when the player reports volume changes. 1639 if (!processingMediaPlayerCallback()) { 1640 Page* page = document()->page(); 1641 float volumeMultiplier = page ? page->mediaVolume() : 1; 1642 1643 m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier); 1644 } 1645 1646 if (renderer()) 1647 renderer()->updateFromElement(); 1648 } 1649 1650 void HTMLMediaElement::updatePlayState() 1651 { 1652 if (!m_player) 1653 return; 1654 1655 if (m_pausedInternal) { 1656 if (!m_player->paused()) 1657 m_player->pause(); 1658 m_playbackProgressTimer.stop(); 1659 return; 1660 } 1661 1662 bool shouldBePlaying = potentiallyPlaying(); 1663 bool playerPaused = m_player->paused(); 1664 if (shouldBePlaying && playerPaused) { 1665 // Set rate before calling play in case the rate was set before the media engine wasn't setup. 1666 // The media engine should just stash the rate since it isn't already playing. 1667 m_player->setRate(m_playbackRate); 1668 m_player->play(); 1669 startPlaybackProgressTimer(); 1670 m_playing = true; 1671 } else if (!shouldBePlaying && !playerPaused) { 1672 m_player->pause(); 1673 m_playbackProgressTimer.stop(); 1674 m_playing = false; 1675 float time = currentTime(); 1676 if (time > m_lastSeekTime) 1677 addPlayedRange(m_lastSeekTime, time); 1678 } else if (couldPlayIfEnoughData() && playerPaused) 1679 m_player->prepareToPlay(); 1680 1681 if (renderer()) 1682 renderer()->updateFromElement(); 1683 } 1684 1685 void HTMLMediaElement::setPausedInternal(bool b) 1686 { 1687 m_pausedInternal = b; 1688 updatePlayState(); 1689 } 1690 1691 void HTMLMediaElement::stopPeriodicTimers() 1692 { 1693 m_progressEventTimer.stop(); 1694 m_playbackProgressTimer.stop(); 1695 } 1696 1697 void HTMLMediaElement::userCancelledLoad() 1698 { 1699 if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) 1700 return; 1701 1702 // If the media data fetching process is aborted by the user: 1703 1704 // 1 - The user agent should cancel the fetching process. 1705 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1706 m_player.clear(); 1707 #endif 1708 stopPeriodicTimers(); 1709 1710 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED. 1711 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); 1712 1713 // 3 - Queue a task to fire a progress event called abort at the media element, in the context 1714 // of the fetching process started by this instance of this algorithm. 1715 scheduleEvent(eventNames().abortEvent); 1716 1717 // 5 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the 1718 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a 1719 // simple event called emptied at the element. Otherwise, set set the element's networkState 1720 // attribute to the NETWORK_IDLE value. 1721 if (m_readyState == HAVE_NOTHING) { 1722 m_networkState = NETWORK_EMPTY; 1723 scheduleEvent(eventNames().emptiedEvent); 1724 } 1725 else 1726 m_networkState = NETWORK_IDLE; 1727 1728 // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. 1729 m_delayingTheLoadEvent = false; 1730 1731 // 7 - Abort the overall resource selection algorithm. 1732 m_currentSourceNode = 0; 1733 } 1734 1735 void HTMLMediaElement::documentWillBecomeInactive() 1736 { 1737 if (m_isFullscreen) 1738 exitFullscreen(); 1739 1740 m_inActiveDocument = false; 1741 userCancelledLoad(); 1742 1743 // Stop the playback without generating events 1744 setPausedInternal(true); 1745 1746 if (renderer()) 1747 renderer()->updateFromElement(); 1748 1749 stopPeriodicTimers(); 1750 cancelPendingEventsAndCallbacks(); 1751 } 1752 1753 void HTMLMediaElement::documentDidBecomeActive() 1754 { 1755 m_inActiveDocument = true; 1756 setPausedInternal(false); 1757 1758 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { 1759 // Restart the load if it was aborted in the middle by moving the document to the page cache. 1760 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to 1761 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). 1762 // This behavior is not specified but it seems like a sensible thing to do. 1763 ExceptionCode ec; 1764 load(processingUserGesture(), ec); 1765 } 1766 1767 if (renderer()) 1768 renderer()->updateFromElement(); 1769 } 1770 1771 void HTMLMediaElement::mediaVolumeDidChange() 1772 { 1773 updateVolume(); 1774 } 1775 1776 const IntRect HTMLMediaElement::screenRect() 1777 { 1778 IntRect elementRect; 1779 if (renderer()) 1780 elementRect = renderer()->view()->frameView()->contentsToScreen(renderer()->absoluteBoundingBoxRect()); 1781 return elementRect; 1782 } 1783 1784 void HTMLMediaElement::defaultEventHandler(Event* event) 1785 { 1786 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1787 RenderObject* r = renderer(); 1788 if (!r || !r->isWidget()) 1789 return; 1790 1791 Widget* widget = toRenderWidget(r)->widget(); 1792 if (widget) 1793 widget->handleEvent(event); 1794 #else 1795 if (renderer() && renderer()->isMedia()) 1796 toRenderMedia(renderer())->forwardEvent(event); 1797 if (event->defaultHandled()) 1798 return; 1799 HTMLElement::defaultEventHandler(event); 1800 #endif 1801 } 1802 1803 bool HTMLMediaElement::processingUserGesture() const 1804 { 1805 Frame* frame = document()->frame(); 1806 FrameLoader* loader = frame ? frame->loader() : 0; 1807 1808 // return 'true' for safety if we don't know the answer 1809 return loader ? loader->isProcessingUserGesture() : true; 1810 } 1811 1812 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 1813 1814 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) 1815 { 1816 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { 1817 togglePlayState(); 1818 return; 1819 } 1820 1821 if (m_player) 1822 m_player->deliverNotification(notification); 1823 } 1824 1825 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) 1826 { 1827 if (m_player) 1828 m_player->setMediaPlayerProxy(proxy); 1829 } 1830 1831 String HTMLMediaElement::initialURL() 1832 { 1833 KURL initialSrc = document()->completeURL(getAttribute(srcAttr)); 1834 1835 if (!initialSrc.isValid()) 1836 initialSrc = selectNextSourceChild(0, DoNothing); 1837 1838 m_currentSrc = initialSrc.string(); 1839 1840 return initialSrc; 1841 } 1842 1843 void HTMLMediaElement::finishParsingChildren() 1844 { 1845 HTMLElement::finishParsingChildren(); 1846 if (!m_player) 1847 m_player = MediaPlayer::create(this); 1848 1849 document()->updateStyleIfNeeded(); 1850 if (m_needWidgetUpdate && renderer()) 1851 toRenderEmbeddedObject(renderer())->updateWidget(true); 1852 } 1853 1854 #endif 1855 1856 void HTMLMediaElement::enterFullscreen() 1857 { 1858 ASSERT(!m_isFullscreen); 1859 if (document() && document()->page()) { 1860 document()->page()->chrome()->client()->enterFullscreenForNode(this); 1861 scheduleEvent(eventNames().webkitbeginfullscreenEvent); 1862 m_isFullscreen = true; 1863 } 1864 } 1865 1866 void HTMLMediaElement::exitFullscreen() 1867 { 1868 ASSERT(m_isFullscreen); 1869 if (document() && document()->page()) { 1870 document()->page()->chrome()->client()->exitFullscreenForNode(this); 1871 scheduleEvent(eventNames().webkitendfullscreenEvent); 1872 } 1873 m_isFullscreen = false; 1874 } 1875 1876 PlatformMedia HTMLMediaElement::platformMedia() const 1877 { 1878 return m_player ? m_player->platformMedia() : NoPlatformMedia; 1879 } 1880 1881 bool HTMLMediaElement::hasClosedCaptions() const 1882 { 1883 return m_player && m_player->hasClosedCaptions(); 1884 } 1885 1886 bool HTMLMediaElement::closedCaptionsVisible() const 1887 { 1888 return m_closedCaptionsVisible; 1889 } 1890 1891 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) 1892 { 1893 if (!m_player ||!hasClosedCaptions()) 1894 return; 1895 1896 m_closedCaptionsVisible = closedCaptionVisible; 1897 m_player->setClosedCaptionsVisible(closedCaptionVisible); 1898 if (renderer()) 1899 renderer()->updateFromElement(); 1900 } 1901 1902 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible) 1903 { 1904 setClosedCaptionsVisible(visible); 1905 } 1906 1907 bool HTMLMediaElement::webkitClosedCaptionsVisible() const 1908 { 1909 return closedCaptionsVisible(); 1910 } 1911 1912 1913 bool HTMLMediaElement::webkitHasClosedCaptions() const 1914 { 1915 return hasClosedCaptions(); 1916 } 1917 1918 } 1919 1920 #endif 1921