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