| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2011 Apple Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions | |
| 6 * are met: | |
| 7 * 1. Redistributions of source code must retain the above copyright | |
| 8 * notice, this list of conditions and the following disclaimer. | |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer in the | |
| 11 * documentation and/or other materials provided with the distribution. | |
| 12 * | |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 24 */ | |
| 25 | |
| 26 #include "config.h" | |
| 27 #include "core/html/MediaController.h" | |
| 28 | |
| 29 #include "bindings/core/v8/ExceptionMessages.h" | |
| 30 #include "bindings/core/v8/ExceptionState.h" | |
| 31 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | |
| 32 #include "core/dom/ExceptionCode.h" | |
| 33 #include "core/dom/ExecutionContext.h" | |
| 34 #include "core/events/Event.h" | |
| 35 #include "core/events/GenericEventQueue.h" | |
| 36 #include "core/html/HTMLMediaElement.h" | |
| 37 #include "core/html/TimeRanges.h" | |
| 38 #include "platform/Clock.h" | |
| 39 #include "wtf/CurrentTime.h" | |
| 40 #include "wtf/StdLibExtras.h" | |
| 41 #include "wtf/text/AtomicString.h" | |
| 42 | |
| 43 namespace blink { | |
| 44 | |
| 45 PassRefPtrWillBeRawPtr<MediaController> MediaController::create(ExecutionContext
* context) | |
| 46 { | |
| 47 return adoptRefWillBeNoop(new MediaController(context)); | |
| 48 } | |
| 49 | |
| 50 MediaController::MediaController(ExecutionContext* context) | |
| 51 : m_paused(false) | |
| 52 , m_defaultPlaybackRate(1) | |
| 53 , m_volume(1) | |
| 54 , m_position(MediaPlayer::invalidTime()) | |
| 55 , m_muted(false) | |
| 56 , m_readyState(HTMLMediaElement::HAVE_NOTHING) | |
| 57 , m_playbackState(WAITING) | |
| 58 , m_pendingEventsQueue(GenericEventQueue::create(this)) | |
| 59 , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired) | |
| 60 , m_clock(Clock::create()) | |
| 61 , m_executionContext(context) | |
| 62 , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired) | |
| 63 , m_previousTimeupdateTime(0) | |
| 64 { | |
| 65 ScriptWrappable::init(this); | |
| 66 } | |
| 67 | |
| 68 MediaController::~MediaController() | |
| 69 { | |
| 70 } | |
| 71 | |
| 72 void MediaController::addMediaElement(HTMLMediaElement* element) | |
| 73 { | |
| 74 ASSERT(element); | |
| 75 ASSERT(!m_mediaElements.contains(element)); | |
| 76 | |
| 77 m_mediaElements.add(element); | |
| 78 bringElementUpToSpeed(element); | |
| 79 } | |
| 80 | |
| 81 void MediaController::removeMediaElement(HTMLMediaElement* element) | |
| 82 { | |
| 83 ASSERT(element); | |
| 84 ASSERT(m_mediaElements.contains(element)); | |
| 85 m_mediaElements.remove(m_mediaElements.find(element)); | |
| 86 } | |
| 87 | |
| 88 PassRefPtrWillBeRawPtr<TimeRanges> MediaController::buffered() const | |
| 89 { | |
| 90 if (m_mediaElements.isEmpty()) | |
| 91 return TimeRanges::create(); | |
| 92 | |
| 93 // The buffered attribute must return a new static normalized TimeRanges obj
ect that represents | |
| 94 // the intersection of the ranges of the media resources of the slaved media
elements that the | |
| 95 // user agent has buffered, at the time the attribute is evaluated. | |
| 96 MediaElementSequence::const_iterator it = m_mediaElements.begin(); | |
| 97 RefPtrWillBeRawPtr<TimeRanges> bufferedRanges = (*it)->buffered(); | |
| 98 for (++it; it != m_mediaElements.end(); ++it) | |
| 99 bufferedRanges->intersectWith((*it)->buffered().get()); | |
| 100 return bufferedRanges; | |
| 101 } | |
| 102 | |
| 103 PassRefPtrWillBeRawPtr<TimeRanges> MediaController::seekable() const | |
| 104 { | |
| 105 if (m_mediaElements.isEmpty()) | |
| 106 return TimeRanges::create(); | |
| 107 | |
| 108 // The seekable attribute must return a new static normalized TimeRanges obj
ect that represents | |
| 109 // the intersection of the ranges of the media resources of the slaved media
elements that the | |
| 110 // user agent is able to seek to, at the time the attribute is evaluated. | |
| 111 MediaElementSequence::const_iterator it = m_mediaElements.begin(); | |
| 112 RefPtrWillBeRawPtr<TimeRanges> seekableRanges = (*it)->seekable(); | |
| 113 for (++it; it != m_mediaElements.end(); ++it) | |
| 114 seekableRanges->intersectWith((*it)->seekable().get()); | |
| 115 return seekableRanges; | |
| 116 } | |
| 117 | |
| 118 PassRefPtrWillBeRawPtr<TimeRanges> MediaController::played() | |
| 119 { | |
| 120 if (m_mediaElements.isEmpty()) | |
| 121 return TimeRanges::create(); | |
| 122 | |
| 123 // The played attribute must return a new static normalized TimeRanges objec
t that represents | |
| 124 // the union of the ranges of the media resources of the slaved media elemen
ts that the | |
| 125 // user agent has so far rendered, at the time the attribute is evaluated. | |
| 126 MediaElementSequence::const_iterator it = m_mediaElements.begin(); | |
| 127 RefPtrWillBeRawPtr<TimeRanges> playedRanges = (*it)->played(); | |
| 128 for (++it; it != m_mediaElements.end(); ++it) | |
| 129 playedRanges->unionWith((*it)->played().get()); | |
| 130 return playedRanges; | |
| 131 } | |
| 132 | |
| 133 double MediaController::duration() const | |
| 134 { | |
| 135 // FIXME: Investigate caching the maximum duration and only updating the cac
hed value | |
| 136 // when the slaved media elements' durations change. | |
| 137 double maxDuration = 0; | |
| 138 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) { | |
| 139 double duration = (*it)->duration(); | |
| 140 if (std::isnan(duration)) | |
| 141 continue; | |
| 142 maxDuration = std::max(maxDuration, duration); | |
| 143 } | |
| 144 return maxDuration; | |
| 145 } | |
| 146 | |
| 147 double MediaController::currentTime() const | |
| 148 { | |
| 149 if (m_mediaElements.isEmpty()) | |
| 150 return 0; | |
| 151 | |
| 152 if (m_position == MediaPlayer::invalidTime()) { | |
| 153 // Some clocks may return times outside the range of [0..duration]. | |
| 154 m_position = std::max(0.0, std::min(duration(), m_clock->currentTime()))
; | |
| 155 m_clearPositionTimer.startOneShot(0, FROM_HERE); | |
| 156 } | |
| 157 | |
| 158 return m_position; | |
| 159 } | |
| 160 | |
| 161 void MediaController::setCurrentTime(double time, ExceptionState& exceptionState
) | |
| 162 { | |
| 163 // When the user agent is to seek the media controller to a particular new p
layback position, | |
| 164 // it must follow these steps: | |
| 165 // If the new playback position is less than zero, then set it to zero. | |
| 166 time = std::max(0.0, time); | |
| 167 | |
| 168 // If the new playback position is greater than the media controller duratio
n, then set it | |
| 169 // to the media controller duration. | |
| 170 time = std::min(time, duration()); | |
| 171 | |
| 172 // Set the media controller position to the new playback position. | |
| 173 m_position = time; | |
| 174 m_clock->setCurrentTime(time); | |
| 175 | |
| 176 // Seek each slaved media element to the new playback position relative to t
he media element timeline. | |
| 177 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) | |
| 178 (*it)->seek(time, exceptionState); | |
| 179 | |
| 180 scheduleTimeupdateEvent(); | |
| 181 } | |
| 182 | |
| 183 void MediaController::unpause() | |
| 184 { | |
| 185 // When the unpause() method is invoked, if the MediaController is a paused
media controller, | |
| 186 if (!m_paused) | |
| 187 return; | |
| 188 | |
| 189 // the user agent must change the MediaController into a playing media contr
oller, | |
| 190 m_paused = false; | |
| 191 // queue a task to fire a simple event named play at the MediaController, | |
| 192 scheduleEvent(EventTypeNames::play); | |
| 193 // and then report the controller state of the MediaController. | |
| 194 reportControllerState(); | |
| 195 } | |
| 196 | |
| 197 void MediaController::play() | |
| 198 { | |
| 199 // When the play() method is invoked, the user agent must invoke the play me
thod of each | |
| 200 // slaved media element in turn, | |
| 201 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) | |
| 202 (*it)->play(); | |
| 203 | |
| 204 // and then invoke the unpause method of the MediaController. | |
| 205 unpause(); | |
| 206 } | |
| 207 | |
| 208 void MediaController::pause() | |
| 209 { | |
| 210 // When the pause() method is invoked, if the MediaController is a playing m
edia controller, | |
| 211 if (m_paused) | |
| 212 return; | |
| 213 | |
| 214 // then the user agent must change the MediaController into a paused media c
ontroller, | |
| 215 m_paused = true; | |
| 216 // queue a task to fire a simple event named pause at the MediaController, | |
| 217 scheduleEvent(EventTypeNames::pause); | |
| 218 // and then report the controller state of the MediaController. | |
| 219 reportControllerState(); | |
| 220 } | |
| 221 | |
| 222 void MediaController::setDefaultPlaybackRate(double rate) | |
| 223 { | |
| 224 if (m_defaultPlaybackRate == rate) | |
| 225 return; | |
| 226 | |
| 227 // The defaultPlaybackRate attribute, on setting, must set the MediaControll
er's media controller | |
| 228 // default playback rate to the new value, | |
| 229 m_defaultPlaybackRate = rate; | |
| 230 | |
| 231 // then queue a task to fire a simple event named ratechange at the MediaCon
troller. | |
| 232 scheduleEvent(EventTypeNames::ratechange); | |
| 233 } | |
| 234 | |
| 235 double MediaController::playbackRate() const | |
| 236 { | |
| 237 return m_clock->playRate(); | |
| 238 } | |
| 239 | |
| 240 void MediaController::setPlaybackRate(double rate) | |
| 241 { | |
| 242 if (m_clock->playRate() == rate) | |
| 243 return; | |
| 244 | |
| 245 // The playbackRate attribute, on setting, must set the MediaController's me
dia controller | |
| 246 // playback rate to the new value, | |
| 247 m_clock->setPlayRate(rate); | |
| 248 | |
| 249 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) | |
| 250 (*it)->updatePlaybackRate(); | |
| 251 | |
| 252 // then queue a task to fire a simple event named ratechange at the MediaCon
troller. | |
| 253 scheduleEvent(EventTypeNames::ratechange); | |
| 254 } | |
| 255 | |
| 256 void MediaController::setVolume(double level, ExceptionState& exceptionState) | |
| 257 { | |
| 258 if (m_volume == level) | |
| 259 return; | |
| 260 | |
| 261 // If the new value is outside the range 0.0 to 1.0 inclusive, then, on sett
ing, an | |
| 262 // IndexSizeError exception must be raised instead. | |
| 263 if (level < 0 || level > 1) { | |
| 264 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::inde
xOutsideRange("volume", level, 0.0, ExceptionMessages::InclusiveBound, 1.0, Exce
ptionMessages::InclusiveBound)); | |
| 265 return; | |
| 266 } | |
| 267 | |
| 268 // The volume attribute, on setting, if the new value is in the range 0.0 to
1.0 inclusive, | |
| 269 // must set the MediaController's media controller volume multiplier to the
new value | |
| 270 m_volume = level; | |
| 271 | |
| 272 // and queue a task to fire a simple event named volumechange at the MediaCo
ntroller. | |
| 273 scheduleEvent(EventTypeNames::volumechange); | |
| 274 | |
| 275 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) | |
| 276 (*it)->updateVolume(); | |
| 277 } | |
| 278 | |
| 279 void MediaController::setMuted(bool flag) | |
| 280 { | |
| 281 if (m_muted == flag) | |
| 282 return; | |
| 283 | |
| 284 // The muted attribute, on setting, must set the MediaController's media con
troller mute override | |
| 285 // to the new value | |
| 286 m_muted = flag; | |
| 287 | |
| 288 // and queue a task to fire a simple event named volumechange at the MediaCo
ntroller. | |
| 289 scheduleEvent(EventTypeNames::volumechange); | |
| 290 | |
| 291 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) | |
| 292 (*it)->updateVolume(); | |
| 293 } | |
| 294 | |
| 295 static const AtomicString& playbackStateWaiting() | |
| 296 { | |
| 297 DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::Constru
ctFromLiteral)); | |
| 298 return waiting; | |
| 299 } | |
| 300 | |
| 301 static const AtomicString& playbackStatePlaying() | |
| 302 { | |
| 303 DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::Constru
ctFromLiteral)); | |
| 304 return playing; | |
| 305 } | |
| 306 | |
| 307 static const AtomicString& playbackStateEnded() | |
| 308 { | |
| 309 DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFr
omLiteral)); | |
| 310 return ended; | |
| 311 } | |
| 312 | |
| 313 const AtomicString& MediaController::playbackState() const | |
| 314 { | |
| 315 switch (m_playbackState) { | |
| 316 case WAITING: | |
| 317 return playbackStateWaiting(); | |
| 318 case PLAYING: | |
| 319 return playbackStatePlaying(); | |
| 320 case ENDED: | |
| 321 return playbackStateEnded(); | |
| 322 default: | |
| 323 ASSERT_NOT_REACHED(); | |
| 324 return nullAtom; | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 void MediaController::reportControllerState() | |
| 329 { | |
| 330 updateReadyState(); | |
| 331 updatePlaybackState(); | |
| 332 } | |
| 333 | |
| 334 static const AtomicString& eventNameForReadyState(HTMLMediaElement::ReadyState s
tate) | |
| 335 { | |
| 336 switch (state) { | |
| 337 case HTMLMediaElement::HAVE_NOTHING: | |
| 338 return EventTypeNames::emptied; | |
| 339 case HTMLMediaElement::HAVE_METADATA: | |
| 340 return EventTypeNames::loadedmetadata; | |
| 341 case HTMLMediaElement::HAVE_CURRENT_DATA: | |
| 342 return EventTypeNames::loadeddata; | |
| 343 case HTMLMediaElement::HAVE_FUTURE_DATA: | |
| 344 return EventTypeNames::canplay; | |
| 345 case HTMLMediaElement::HAVE_ENOUGH_DATA: | |
| 346 return EventTypeNames::canplaythrough; | |
| 347 default: | |
| 348 ASSERT_NOT_REACHED(); | |
| 349 return nullAtom; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 void MediaController::updateReadyState() | |
| 354 { | |
| 355 ReadyState oldReadyState = m_readyState; | |
| 356 ReadyState newReadyState; | |
| 357 | |
| 358 if (m_mediaElements.isEmpty()) { | |
| 359 // If the MediaController has no slaved media elements, let new readines
s state be 0. | |
| 360 newReadyState = HTMLMediaElement::HAVE_NOTHING; | |
| 361 } else { | |
| 362 // Otherwise, let it have the lowest value of the readyState IDL attribu
tes of all of its | |
| 363 // slaved media elements. | |
| 364 MediaElementSequence::const_iterator it = m_mediaElements.begin(); | |
| 365 newReadyState = (*it)->readyState(); | |
| 366 for (++it; it != m_mediaElements.end(); ++it) | |
| 367 newReadyState = std::min(newReadyState, (*it)->readyState()); | |
| 368 } | |
| 369 | |
| 370 if (newReadyState == oldReadyState) | |
| 371 return; | |
| 372 | |
| 373 // If the MediaController's most recently reported readiness state is greate
r than new readiness | |
| 374 // state then queue a task to fire a simple event at the MediaController obj
ect, whose name is the | |
| 375 // event name corresponding to the value of new readiness state given in the
table below. [omitted] | |
| 376 if (oldReadyState > newReadyState) { | |
| 377 scheduleEvent(eventNameForReadyState(newReadyState)); | |
| 378 return; | |
| 379 } | |
| 380 | |
| 381 // If the MediaController's most recently reported readiness state is less t
han the new readiness | |
| 382 // state, then run these substeps: | |
| 383 // 1. Let next state be the MediaController's most recently reported readine
ss state. | |
| 384 ReadyState nextState = oldReadyState; | |
| 385 do { | |
| 386 // 2. Loop: Increment next state by one. | |
| 387 nextState = static_cast<ReadyState>(nextState + 1); | |
| 388 // 3. Queue a task to fire a simple event at the MediaController object,
whose name is the | |
| 389 // event name corresponding to the value of next state given in the tabl
e below. [omitted] | |
| 390 scheduleEvent(eventNameForReadyState(nextState)); | |
| 391 // If next state is less than new readiness state, then return to the st
ep labeled loop | |
| 392 } while (nextState < newReadyState); | |
| 393 | |
| 394 // Let the MediaController's most recently reported readiness state be new r
eadiness state. | |
| 395 m_readyState = newReadyState; | |
| 396 } | |
| 397 | |
| 398 void MediaController::updatePlaybackState() | |
| 399 { | |
| 400 PlaybackState oldPlaybackState = m_playbackState; | |
| 401 PlaybackState newPlaybackState; | |
| 402 | |
| 403 // Initialize new playback state by setting it to the state given for the fi
rst matching | |
| 404 // condition from the following list: | |
| 405 if (m_mediaElements.isEmpty()) { | |
| 406 // If the MediaController has no slaved media elements | |
| 407 // Let new playback state be waiting. | |
| 408 newPlaybackState = WAITING; | |
| 409 } else if (hasEnded()) { | |
| 410 // If all of the MediaController's slaved media elements have ended play
back and the media | |
| 411 // controller playback rate is positive or zero | |
| 412 // Let new playback state be ended. | |
| 413 newPlaybackState = ENDED; | |
| 414 } else if (isBlocked()) { | |
| 415 // If the MediaController is a blocked media controller | |
| 416 // Let new playback state be waiting. | |
| 417 newPlaybackState = WAITING; | |
| 418 } else { | |
| 419 // Otherwise | |
| 420 // Let new playback state be playing. | |
| 421 newPlaybackState = PLAYING; | |
| 422 } | |
| 423 | |
| 424 // If the MediaController's most recently reported playback state is not equ
al to new playback state | |
| 425 if (newPlaybackState == oldPlaybackState) | |
| 426 return; | |
| 427 | |
| 428 // and the new playback state is ended, | |
| 429 if (newPlaybackState == ENDED) { | |
| 430 // then queue a task that, if the MediaController object is a playing me
dia controller, and | |
| 431 // all of the MediaController's slaved media elements have still ended p
layback, and the | |
| 432 // media controller playback rate is still positive or zero, | |
| 433 if (!m_paused && hasEnded()) { | |
| 434 // changes the MediaController object to a paused media controller | |
| 435 m_paused = true; | |
| 436 | |
| 437 // and then fires a simple event named pause at the MediaController
object. | |
| 438 scheduleEvent(EventTypeNames::pause); | |
| 439 } | |
| 440 } | |
| 441 | |
| 442 // If the MediaController's most recently reported playback state is not equ
al to new playback state | |
| 443 // then queue a task to fire a simple event at the MediaController object, w
hose name is playing | |
| 444 // if new playback state is playing, ended if new playback state is ended, a
nd waiting otherwise. | |
| 445 AtomicString eventName; | |
| 446 switch (newPlaybackState) { | |
| 447 case WAITING: | |
| 448 eventName = EventTypeNames::waiting; | |
| 449 m_clock->stop(); | |
| 450 m_timeupdateTimer.stop(); | |
| 451 break; | |
| 452 case ENDED: | |
| 453 eventName = EventTypeNames::ended; | |
| 454 m_clock->stop(); | |
| 455 m_timeupdateTimer.stop(); | |
| 456 break; | |
| 457 case PLAYING: | |
| 458 eventName = EventTypeNames::playing; | |
| 459 m_clock->start(); | |
| 460 startTimeupdateTimer(); | |
| 461 break; | |
| 462 default: | |
| 463 ASSERT_NOT_REACHED(); | |
| 464 } | |
| 465 scheduleEvent(eventName); | |
| 466 | |
| 467 // Let the MediaController's most recently reported playback state be new pl
ayback state. | |
| 468 m_playbackState = newPlaybackState; | |
| 469 | |
| 470 updateMediaElements(); | |
| 471 } | |
| 472 | |
| 473 void MediaController::updateMediaElements() | |
| 474 { | |
| 475 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) | |
| 476 (*it)->updatePlayState(); | |
| 477 } | |
| 478 | |
| 479 void MediaController::bringElementUpToSpeed(HTMLMediaElement* element) | |
| 480 { | |
| 481 ASSERT(element); | |
| 482 ASSERT(m_mediaElements.contains(element)); | |
| 483 | |
| 484 // When the user agent is to bring a media element up to speed with its new
media controller, | |
| 485 // it must seek that media element to the MediaController's media controller
position relative | |
| 486 // to the media element's timeline. | |
| 487 element->seek(currentTime(), IGNORE_EXCEPTION); | |
| 488 | |
| 489 // Update volume to take controller volume and mute into account. | |
| 490 element->updateVolume(); | |
| 491 } | |
| 492 | |
| 493 bool MediaController::isRestrained() const | |
| 494 { | |
| 495 ASSERT(!m_mediaElements.isEmpty()); | |
| 496 | |
| 497 // A MediaController is a restrained media controller if the MediaController
is a playing media | |
| 498 // controller, | |
| 499 if (m_paused) | |
| 500 return false; | |
| 501 | |
| 502 bool anyAutoplayingAndPaused = false; | |
| 503 bool allPaused = true; | |
| 504 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) { | |
| 505 HTMLMediaElement* element = *it; | |
| 506 | |
| 507 // and none of its slaved media elements are blocked media elements, | |
| 508 if (element->isBlocked()) | |
| 509 return false; | |
| 510 | |
| 511 if (element->isAutoplaying() && element->paused()) | |
| 512 anyAutoplayingAndPaused = true; | |
| 513 | |
| 514 if (!element->paused()) | |
| 515 allPaused = false; | |
| 516 } | |
| 517 | |
| 518 // but either at least one of its slaved media elements whose autoplaying fl
ag is true still has | |
| 519 // its paused attribute set to true, or, all of its slaved media elements ha
ve their paused | |
| 520 // attribute set to true. | |
| 521 return anyAutoplayingAndPaused || allPaused; | |
| 522 } | |
| 523 | |
| 524 bool MediaController::isBlocked() const | |
| 525 { | |
| 526 ASSERT(!m_mediaElements.isEmpty()); | |
| 527 | |
| 528 // A MediaController is a blocked media controller if the MediaController is
a paused media | |
| 529 // controller, | |
| 530 if (m_paused) | |
| 531 return true; | |
| 532 | |
| 533 bool allPaused = true; | |
| 534 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) { | |
| 535 HTMLMediaElement* element = *it; | |
| 536 | |
| 537 // or if any of its slaved media elements are blocked media elements, | |
| 538 if (element->isBlocked()) | |
| 539 return true; | |
| 540 | |
| 541 // or if any of its slaved media elements whose autoplaying flag is true
still have their | |
| 542 // paused attribute set to true, | |
| 543 if (element->isAutoplaying() && element->paused()) | |
| 544 return true; | |
| 545 | |
| 546 if (!element->paused()) | |
| 547 allPaused = false; | |
| 548 } | |
| 549 | |
| 550 // or if all of its slaved media elements have their paused attribute set to
true. | |
| 551 return allPaused; | |
| 552 } | |
| 553 | |
| 554 bool MediaController::hasEnded() const | |
| 555 { | |
| 556 // If the ... media controller playback rate is positive or zero | |
| 557 if (m_clock->playRate() < 0) | |
| 558 return false; | |
| 559 | |
| 560 // [and] all of the MediaController's slaved media elements have ended playb
ack ... let new | |
| 561 // playback state be ended. | |
| 562 if (m_mediaElements.isEmpty()) | |
| 563 return false; | |
| 564 | |
| 565 bool allHaveEnded = true; | |
| 566 for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it !
= m_mediaElements.end(); ++it) { | |
| 567 if (!(*it)->ended()) | |
| 568 allHaveEnded = false; | |
| 569 } | |
| 570 return allHaveEnded; | |
| 571 } | |
| 572 | |
| 573 void MediaController::scheduleEvent(const AtomicString& eventName) | |
| 574 { | |
| 575 m_pendingEventsQueue->enqueueEvent(Event::createCancelable(eventName)); | |
| 576 } | |
| 577 | |
| 578 void MediaController::clearPositionTimerFired(Timer<MediaController>*) | |
| 579 { | |
| 580 m_position = MediaPlayer::invalidTime(); | |
| 581 } | |
| 582 | |
| 583 const AtomicString& MediaController::interfaceName() const | |
| 584 { | |
| 585 return EventTargetNames::MediaController; | |
| 586 } | |
| 587 | |
| 588 // The spec says to fire periodic timeupdate events (those sent while playing) e
very | |
| 589 // "15 to 250ms", we choose the slowest frequency | |
| 590 static const double maxTimeupdateEventFrequency = 0.25; | |
| 591 | |
| 592 void MediaController::startTimeupdateTimer() | |
| 593 { | |
| 594 if (m_timeupdateTimer.isActive()) | |
| 595 return; | |
| 596 | |
| 597 m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HERE); | |
| 598 } | |
| 599 | |
| 600 void MediaController::timeupdateTimerFired(Timer<MediaController>*) | |
| 601 { | |
| 602 scheduleTimeupdateEvent(); | |
| 603 } | |
| 604 | |
| 605 void MediaController::scheduleTimeupdateEvent() | |
| 606 { | |
| 607 double now = WTF::currentTime(); | |
| 608 double timedelta = now - m_previousTimeupdateTime; | |
| 609 | |
| 610 if (timedelta < maxTimeupdateEventFrequency) | |
| 611 return; | |
| 612 | |
| 613 scheduleEvent(EventTypeNames::timeupdate); | |
| 614 m_previousTimeupdateTime = now; | |
| 615 } | |
| 616 | |
| 617 void MediaController::trace(Visitor* visitor) | |
| 618 { | |
| 619 #if ENABLE(OILPAN) | |
| 620 visitor->trace(m_mediaElements); | |
| 621 visitor->trace(m_pendingEventsQueue); | |
| 622 visitor->trace(m_executionContext); | |
| 623 #endif | |
| 624 EventTargetWithInlineData::trace(visitor); | |
| 625 } | |
| 626 | |
| 627 } | |
| OLD | NEW |