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
43namespace blink {
44
45PassRefPtrWillBeRawPtr<MediaController> MediaController::create(ExecutionContext* context)
46{
47    return adoptRefWillBeNoop(new MediaController(context));
48}
49
50MediaController::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}
66
67MediaController::~MediaController()
68{
69}
70
71void 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
80void MediaController::removeMediaElement(HTMLMediaElement* element)
81{
82    ASSERT(element);
83    ASSERT(m_mediaElements.contains(element));
84    m_mediaElements.remove(m_mediaElements.find(element));
85}
86
87PassRefPtrWillBeRawPtr<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 object 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    RefPtrWillBeRawPtr<TimeRanges> bufferedRanges = (*it)->buffered();
97    for (++it; it != m_mediaElements.end(); ++it)
98        bufferedRanges->intersectWith((*it)->buffered().get());
99    return bufferedRanges;
100}
101
102PassRefPtrWillBeRawPtr<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 object 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    RefPtrWillBeRawPtr<TimeRanges> seekableRanges = (*it)->seekable();
112    for (++it; it != m_mediaElements.end(); ++it)
113        seekableRanges->intersectWith((*it)->seekable().get());
114    return seekableRanges;
115}
116
117PassRefPtrWillBeRawPtr<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 object that represents
123    // the union of the ranges of the media resources of the slaved media elements 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    RefPtrWillBeRawPtr<TimeRanges> playedRanges = (*it)->played();
127    for (++it; it != m_mediaElements.end(); ++it)
128        playedRanges->unionWith((*it)->played().get());
129    return playedRanges;
130}
131
132double MediaController::duration() const
133{
134    // FIXME: Investigate caching the maximum duration and only updating the cached value
135    // when the slaved media elements' durations change.
136    double maxDuration = 0;
137    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
138        double duration = (*it)->duration();
139        if (std::isnan(duration))
140            continue;
141        maxDuration = std::max(maxDuration, duration);
142    }
143    return maxDuration;
144}
145
146double MediaController::currentTime() const
147{
148    if (m_mediaElements.isEmpty())
149        return 0;
150
151    if (m_position == MediaPlayer::invalidTime()) {
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, FROM_HERE);
155    }
156
157    return m_position;
158}
159
160void MediaController::setCurrentTime(double time)
161{
162    // When the user agent is to seek the media controller to a particular new playback 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 duration, 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 the media element timeline.
176    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
177        (*it)->seek(time);
178
179    scheduleTimeupdateEvent();
180}
181
182void 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 controller,
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
196void MediaController::play()
197{
198    // When the play() method is invoked, the user agent must invoke the play method of each
199    // slaved media element in turn,
200    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
201        (*it)->play();
202
203    // and then invoke the unpause method of the MediaController.
204    unpause();
205}
206
207void MediaController::pause()
208{
209    // When the pause() method is invoked, if the MediaController is a playing media controller,
210    if (m_paused)
211        return;
212
213    // then the user agent must change the MediaController into a paused media controller,
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
221void MediaController::setDefaultPlaybackRate(double rate)
222{
223    if (m_defaultPlaybackRate == rate)
224        return;
225
226    // The defaultPlaybackRate attribute, on setting, must set the MediaController'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 MediaController.
231    scheduleEvent(EventTypeNames::ratechange);
232}
233
234double MediaController::playbackRate() const
235{
236    return m_clock->playRate();
237}
238
239void 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 media controller
245    // playback rate to the new value,
246    m_clock->setPlayRate(rate);
247
248    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
249        (*it)->updatePlaybackRate();
250
251    // then queue a task to fire a simple event named ratechange at the MediaController.
252    scheduleEvent(EventTypeNames::ratechange);
253}
254
255void 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 setting, an
261    // IndexSizeError exception must be raised instead.
262    if (level < 0 || level > 1) {
263        exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("volume", level, 0.0, ExceptionMessages::InclusiveBound, 1.0, ExceptionMessages::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 MediaController.
272    scheduleEvent(EventTypeNames::volumechange);
273
274    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
275        (*it)->updateVolume();
276}
277
278void 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 controller 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 MediaController.
288    scheduleEvent(EventTypeNames::volumechange);
289
290    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
291        (*it)->updateVolume();
292}
293
294static const AtomicString& playbackStateWaiting()
295{
296    DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
297    return waiting;
298}
299
300static const AtomicString& playbackStatePlaying()
301{
302    DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
303    return playing;
304}
305
306static const AtomicString& playbackStateEnded()
307{
308    DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
309    return ended;
310}
311
312const 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
327void MediaController::reportControllerState()
328{
329    updateReadyState();
330    updatePlaybackState();
331}
332
333static const AtomicString& eventNameForReadyState(HTMLMediaElement::ReadyState state)
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
352void 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 readiness state be 0.
359        newReadyState = HTMLMediaElement::HAVE_NOTHING;
360    } else {
361        // Otherwise, let it have the lowest value of the readyState IDL attributes 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 greater than new readiness
373    // state then queue a task to fire a simple event at the MediaController object, 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 than the new readiness
381    // state, then run these substeps:
382    // 1. Let next state be the MediaController's most recently reported readiness 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 table below. [omitted]
389        scheduleEvent(eventNameForReadyState(nextState));
390        // If next state is less than new readiness state, then return to the step labeled loop
391    } while (nextState < newReadyState);
392
393    // Let the MediaController's most recently reported readiness state be new readiness state.
394    m_readyState = newReadyState;
395}
396
397void 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 first 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 playback 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 equal 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 media controller, and
430        // all of the MediaController's slaved media elements have still ended playback, 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 equal to new playback state
442    // then queue a task to fire a simple event at the MediaController object, whose name is playing
443    // if new playback state is playing, ended if new playback state is ended, and 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 playback state.
467    m_playbackState = newPlaybackState;
468
469    updateMediaElements();
470}
471
472void MediaController::updateMediaElements()
473{
474    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
475        (*it)->updatePlayState();
476}
477
478void 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
492bool 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 (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
504        HTMLMediaElement* element = *it;
505
506        if (element->isAutoplaying() && element->paused())
507            anyAutoplayingAndPaused = true;
508
509        if (!element->paused())
510            allPaused = false;
511    }
512
513    // but either at least one of its slaved media elements whose autoplaying flag is true still has
514    // its paused attribute set to true, or, all of its slaved media elements have their paused
515    // attribute set to true.
516    return anyAutoplayingAndPaused || allPaused;
517}
518
519bool MediaController::isBlocked() const
520{
521    ASSERT(!m_mediaElements.isEmpty());
522
523    // A MediaController is a blocked media controller if the MediaController is a paused media
524    // controller,
525    if (m_paused)
526        return true;
527
528    bool allPaused = true;
529    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
530        HTMLMediaElement* element = *it;
531
532        // or if any of its slaved media elements are blocked media elements,
533        if (element->isBlocked())
534            return true;
535
536        // or if any of its slaved media elements whose autoplaying flag is true still have their
537        // paused attribute set to true,
538        if (element->isAutoplaying() && element->paused())
539            return true;
540
541        if (!element->paused())
542            allPaused = false;
543    }
544
545    // or if all of its slaved media elements have their paused attribute set to true.
546    return allPaused;
547}
548
549bool MediaController::hasEnded() const
550{
551    // If the ... media controller playback rate is positive or zero
552    if (m_clock->playRate() < 0)
553        return false;
554
555    // [and] all of the MediaController's slaved media elements have ended playback ... let new
556    // playback state be ended.
557    if (m_mediaElements.isEmpty())
558        return false;
559
560    bool allHaveEnded = true;
561    for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
562        if (!(*it)->ended())
563            allHaveEnded = false;
564    }
565    return allHaveEnded;
566}
567
568void MediaController::scheduleEvent(const AtomicString& eventName)
569{
570    m_pendingEventsQueue->enqueueEvent(Event::createCancelable(eventName));
571}
572
573void MediaController::clearPositionTimerFired(Timer<MediaController>*)
574{
575    m_position = MediaPlayer::invalidTime();
576}
577
578const AtomicString& MediaController::interfaceName() const
579{
580    return EventTargetNames::MediaController;
581}
582
583// The spec says to fire periodic timeupdate events (those sent while playing) every
584// "15 to 250ms", we choose the slowest frequency
585static const double maxTimeupdateEventFrequency = 0.25;
586
587void MediaController::startTimeupdateTimer()
588{
589    if (m_timeupdateTimer.isActive())
590        return;
591
592    m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HERE);
593}
594
595void MediaController::timeupdateTimerFired(Timer<MediaController>*)
596{
597    scheduleTimeupdateEvent();
598}
599
600void MediaController::scheduleTimeupdateEvent()
601{
602    double now = WTF::currentTime();
603    double timedelta = now - m_previousTimeupdateTime;
604
605    if (timedelta < maxTimeupdateEventFrequency)
606        return;
607
608    scheduleEvent(EventTypeNames::timeupdate);
609    m_previousTimeupdateTime = now;
610}
611
612void MediaController::trace(Visitor* visitor)
613{
614#if ENABLE(OILPAN)
615    visitor->trace(m_mediaElements);
616    visitor->trace(m_pendingEventsQueue);
617    visitor->trace(m_executionContext);
618#endif
619    EventTargetWithInlineData::trace(visitor);
620}
621
622}
623