[WebIDL] Add support for converting dictionaries to JS
[WebKit-https.git] / Source / WebCore / html / MediaController.cpp
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 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 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 "MediaController.h"
30
31 #include "Clock.h"
32 #include "EventNames.h"
33 #include "ExceptionCode.h"
34 #include "HTMLMediaElement.h"
35 #include "TimeRanges.h"
36 #include <wtf/CurrentTime.h>
37 #include <wtf/StdLibExtras.h>
38 #include <wtf/text/AtomicString.h>
39
40 using namespace WebCore;
41
42 Ref<MediaController> MediaController::create(ScriptExecutionContext& context)
43 {
44     return adoptRef(*new MediaController(context));
45 }
46
47 MediaController::MediaController(ScriptExecutionContext& context)
48     : m_paused(false)
49     , m_defaultPlaybackRate(1)
50     , m_volume(1)
51     , m_position(MediaPlayer::invalidTime())
52     , m_muted(false)
53     , m_readyState(HAVE_NOTHING)
54     , m_playbackState(WAITING)
55     , m_asyncEventTimer(*this, &MediaController::asyncEventTimerFired)
56     , m_clearPositionTimer(*this, &MediaController::clearPositionTimerFired)
57     , m_closedCaptionsVisible(false)
58     , m_clock(Clock::create())
59     , m_scriptExecutionContext(context)
60     , m_timeupdateTimer(*this, &MediaController::scheduleTimeupdateEvent)
61     , m_previousTimeupdateTime(0)
62 {
63 }
64
65 MediaController::~MediaController()
66 {
67 }
68
69 void MediaController::addMediaElement(HTMLMediaElement& element)
70 {
71     ASSERT(!m_mediaElements.contains(&element));
72
73     m_mediaElements.append(&element);
74     bringElementUpToSpeed(element);
75 }
76
77 void MediaController::removeMediaElement(HTMLMediaElement& element)
78 {
79     ASSERT(m_mediaElements.contains(&element));
80     m_mediaElements.remove(m_mediaElements.find(&element));
81 }
82
83 bool MediaController::containsMediaElement(HTMLMediaElement& element) const
84 {
85     return m_mediaElements.contains(&element);
86 }
87
88 Ref<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 object 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     Ref<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered();
97     for (size_t index = 1; index < m_mediaElements.size(); ++index)
98         bufferedRanges->intersectWith(m_mediaElements[index]->buffered());
99     return bufferedRanges;
100 }
101
102 Ref<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     Ref<TimeRanges> seekableRanges = m_mediaElements.first()->seekable();
111     for (size_t index = 1; index < m_mediaElements.size(); ++index)
112         seekableRanges->intersectWith(m_mediaElements[index]->seekable());
113     return seekableRanges;
114 }
115
116 Ref<TimeRanges> MediaController::played()
117 {
118     if (m_mediaElements.isEmpty())
119         return TimeRanges::create();
120
121     // The played attribute must return a new static normalized TimeRanges object that represents 
122     // the union of the ranges of the media resources of the slaved media elements that the 
123     // user agent has so far rendered, at the time the attribute is evaluated.
124     Ref<TimeRanges> playedRanges = m_mediaElements.first()->played();
125     for (size_t index = 1; index < m_mediaElements.size(); ++index)
126         playedRanges->unionWith(m_mediaElements[index]->played());
127     return playedRanges;
128 }
129
130 double MediaController::duration() const
131 {
132     // FIXME: Investigate caching the maximum duration and only updating the cached value
133     // when the slaved media elements' durations change.
134     double maxDuration = 0;
135     for (auto& mediaElement : m_mediaElements) {
136         double duration = mediaElement->duration();
137         if (std::isnan(duration))
138             continue;
139         maxDuration = std::max(maxDuration, duration);
140     }
141     return maxDuration;
142 }
143
144 double MediaController::currentTime() const
145 {
146     if (m_mediaElements.isEmpty())
147         return 0;
148
149     if (m_position == MediaPlayer::invalidTime()) {
150         // Some clocks may return times outside the range of [0..duration].
151         m_position = std::max<double>(0, std::min(duration(), m_clock->currentTime()));
152         m_clearPositionTimer.startOneShot(0);
153     }
154
155     return m_position;
156 }
157
158 void MediaController::setCurrentTime(double time)
159 {
160     // When the user agent is to seek the media controller to a particular new playback position, 
161     // it must follow these steps:
162     // If the new playback position is less than zero, then set it to zero.
163     time = std::max(0.0, time);
164     
165     // If the new playback position is greater than the media controller duration, then set it 
166     // to the media controller duration.
167     time = std::min(time, duration());
168     
169     // Set the media controller position to the new playback position.
170     m_clock->setCurrentTime(time);
171     
172     // Seek each slaved media element to the new playback position relative to the media element timeline.
173     for (auto& mediaElement : m_mediaElements)
174         mediaElement->seek(MediaTime::createWithDouble(time));
175
176     scheduleTimeupdateEvent();
177     m_resetCurrentTimeInNextPlay = false;
178 }
179
180 void MediaController::unpause()
181 {
182     // When the unpause() method is invoked, if the MediaController is a paused media controller,
183     if (!m_paused)
184         return;
185     // the user agent must change the MediaController into a playing media controller,
186     m_paused = false;
187     // queue a task to fire a simple event named play at the MediaController,
188     scheduleEvent(eventNames().playEvent);
189     // and then report the controller state of the MediaController.
190     reportControllerState();
191 }
192
193 void MediaController::play()
194 {
195     // When the play() method is invoked, the user agent must invoke the play method of each
196     // slaved media element in turn,
197     for (auto& mediaElement : m_mediaElements)
198         mediaElement->play();
199
200     // and then invoke the unpause method of the MediaController.
201     unpause();
202 }
203
204 void MediaController::pause()
205 {
206     // When the pause() method is invoked, if the MediaController is a playing media controller,
207     if (m_paused)
208         return;
209
210     // then the user agent must change the MediaController into a paused media controller,
211     m_paused = true;
212     // queue a task to fire a simple event named pause at the MediaController,
213     scheduleEvent(eventNames().pauseEvent);
214     // and then report the controller state of the MediaController.
215     reportControllerState();
216 }
217
218 void MediaController::setDefaultPlaybackRate(double rate)
219 {
220     if (m_defaultPlaybackRate == rate)
221         return;
222
223     // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller
224     // default playback rate to the new value,
225     m_defaultPlaybackRate = rate;
226
227     // then queue a task to fire a simple event named ratechange at the MediaController.
228     scheduleEvent(eventNames().ratechangeEvent);
229 }
230
231 double MediaController::playbackRate() const
232 {
233     return m_clock->playRate();
234 }
235
236 void MediaController::setPlaybackRate(double rate)
237 {
238     if (m_clock->playRate() == rate)
239         return;
240
241     // The playbackRate attribute, on setting, must set the MediaController's media controller 
242     // playback rate to the new value,
243     m_clock->setPlayRate(rate);
244
245     for (auto& mediaElement : m_mediaElements)
246         mediaElement->updatePlaybackRate();
247
248     // then queue a task to fire a simple event named ratechange at the MediaController.
249     scheduleEvent(eventNames().ratechangeEvent);
250 }
251
252 ExceptionOr<void> MediaController::setVolume(double level)
253 {
254     if (m_volume == level)
255         return { };
256
257     // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an 
258     // IndexSizeError exception must be raised instead.
259     if (!(level >= 0 && level <= 1))
260         return Exception { INDEX_SIZE_ERR };
261
262     // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive,
263     // must set the MediaController's media controller volume multiplier to the new value
264     m_volume = level;
265
266     // and queue a task to fire a simple event named volumechange at the MediaController.
267     scheduleEvent(eventNames().volumechangeEvent);
268
269     for (auto& mediaElement : m_mediaElements)
270         mediaElement->updateVolume();
271
272     return { };
273 }
274
275 void MediaController::setMuted(bool flag)
276 {
277     if (m_muted == flag)
278         return;
279
280     // The muted attribute, on setting, must set the MediaController's media controller mute override
281     // to the new value
282     m_muted = flag;
283
284     // and queue a task to fire a simple event named volumechange at the MediaController.
285     scheduleEvent(eventNames().volumechangeEvent);
286
287     for (auto& mediaElement : m_mediaElements)
288         mediaElement->updateVolume();
289 }
290
291 static const AtomicString& playbackStateWaiting()
292 {
293     static NeverDestroyed<AtomicString> waiting("waiting", AtomicString::ConstructFromLiteral);
294     return waiting;
295 }
296
297 static const AtomicString& playbackStatePlaying()
298 {
299     static NeverDestroyed<AtomicString> playing("playing", AtomicString::ConstructFromLiteral);
300     return playing;
301 }
302
303 static const AtomicString& playbackStateEnded()
304 {
305     static NeverDestroyed<AtomicString> ended("ended", AtomicString::ConstructFromLiteral);
306     return ended;
307 }
308
309 const AtomicString& MediaController::playbackState() const
310 {
311     switch (m_playbackState) {
312     case WAITING:
313         return playbackStateWaiting();
314     case PLAYING:
315         return playbackStatePlaying();
316     case ENDED:
317         return playbackStateEnded();
318     default:
319         ASSERT_NOT_REACHED();
320         return nullAtom;
321     }
322 }
323
324 void MediaController::reportControllerState()
325 {
326     updateReadyState();
327     updatePlaybackState();
328 }
329
330 static AtomicString eventNameForReadyState(MediaControllerInterface::ReadyState state)
331 {
332     switch (state) {
333     case MediaControllerInterface::HAVE_NOTHING:
334         return eventNames().emptiedEvent;
335     case MediaControllerInterface::HAVE_METADATA:
336         return eventNames().loadedmetadataEvent;
337     case MediaControllerInterface::HAVE_CURRENT_DATA:
338         return eventNames().loadeddataEvent;
339     case MediaControllerInterface::HAVE_FUTURE_DATA:
340         return eventNames().canplayEvent;
341     case MediaControllerInterface::HAVE_ENOUGH_DATA:
342         return eventNames().canplaythroughEvent;
343     default:
344         ASSERT_NOT_REACHED();
345         return nullAtom;
346     }
347 }
348
349 void MediaController::updateReadyState()
350 {
351     ReadyState oldReadyState = m_readyState;
352     ReadyState newReadyState;
353     
354     if (m_mediaElements.isEmpty()) {
355         // If the MediaController has no slaved media elements, let new readiness state be 0.
356         newReadyState = HAVE_NOTHING;
357     } else {
358         // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its
359         // slaved media elements.
360         newReadyState = m_mediaElements.first()->readyState();
361         for (size_t index = 1; index < m_mediaElements.size(); ++index)
362             newReadyState = std::min(newReadyState, m_mediaElements[index]->readyState());
363     }
364
365     if (newReadyState == oldReadyState) 
366         return;
367
368     // If the MediaController's most recently reported readiness state is greater than new readiness 
369     // state then queue a task to fire a simple event at the MediaController object, whose name is the
370     // event name corresponding to the value of new readiness state given in the table below. [omitted]
371     if (oldReadyState > newReadyState) {
372         scheduleEvent(eventNameForReadyState(newReadyState));
373         return;
374     }
375
376     // If the MediaController's most recently reported readiness state is less than the new readiness
377     // state, then run these substeps:
378     // 1. Let next state be the MediaController's most recently reported readiness state.
379     ReadyState nextState = oldReadyState;
380     do {
381         // 2. Loop: Increment next state by one.
382         nextState = static_cast<ReadyState>(nextState + 1);
383         // 3. Queue a task to fire a simple event at the MediaController object, whose name is the
384         // event name corresponding to the value of next state given in the table below. [omitted]
385         scheduleEvent(eventNameForReadyState(nextState));        
386         // If next state is less than new readiness state, then return to the step labeled loop
387     } while (nextState < newReadyState);
388
389     // Let the MediaController's most recently reported readiness state be new readiness state.
390     m_readyState = newReadyState;
391 }
392
393 void MediaController::updatePlaybackState()
394 {
395     PlaybackState oldPlaybackState = m_playbackState;
396     PlaybackState newPlaybackState;
397
398     // Initialize new playback state by setting it to the state given for the first matching 
399     // condition from the following list:
400     if (m_mediaElements.isEmpty()) {
401         // If the MediaController has no slaved media elements
402         // Let new playback state be waiting.
403         newPlaybackState = WAITING;
404     } else if (hasEnded()) {
405         // If all of the MediaController's slaved media elements have ended playback and the media
406         // controller playback rate is positive or zero
407         // Let new playback state be ended.
408         newPlaybackState = ENDED;
409     } else if (isBlocked()) {
410         // If the MediaController is a blocked media controller
411         // Let new playback state be waiting.
412         newPlaybackState = WAITING;
413     } else {
414         // Otherwise
415         // Let new playback state be playing.
416         newPlaybackState = PLAYING;
417     }
418
419     // If the MediaController's most recently reported playback state is not equal to new playback state
420     if (newPlaybackState == oldPlaybackState)
421         return;
422
423     // and the new playback state is ended,
424     if (newPlaybackState == ENDED) {
425         // then queue a task that, if the MediaController object is a playing media controller, and 
426         // all of the MediaController's slaved media elements have still ended playback, and the 
427         // media controller playback rate is still positive or zero, 
428         if (!m_paused && hasEnded()) {
429             // changes the MediaController object to a paused media controller
430             m_paused = true;
431
432             // and then fires a simple event named pause at the MediaController object.
433             scheduleEvent(eventNames().pauseEvent);
434         }
435     }
436
437     // If the MediaController's most recently reported playback state is not equal to new playback state
438     // then queue a task to fire a simple event at the MediaController object, whose name is playing 
439     // if new playback state is playing, ended if new playback state is ended, and waiting otherwise.
440     AtomicString eventName;
441     switch (newPlaybackState) {
442     case WAITING:
443         eventName = eventNames().waitingEvent;
444         m_clock->stop();
445         m_timeupdateTimer.stop();
446         break;
447     case ENDED:
448         eventName = eventNames().endedEvent;
449         m_resetCurrentTimeInNextPlay = true;
450         m_clock->stop();
451         m_timeupdateTimer.stop();
452         break;
453     case PLAYING:
454         if (m_resetCurrentTimeInNextPlay) {
455             m_resetCurrentTimeInNextPlay = false;
456             m_clock->setCurrentTime(0);
457         }
458         eventName = eventNames().playingEvent;
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 playback state.
468     m_playbackState = newPlaybackState;
469
470     updateMediaElements();
471 }
472
473 void MediaController::updateMediaElements()
474 {
475     for (auto& mediaElement : m_mediaElements)
476         mediaElement->updatePlayState();
477 }
478
479 void MediaController::bringElementUpToSpeed(HTMLMediaElement& element)
480 {
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.seekInternal(MediaTime::createWithDouble(currentTime()));
487 }
488
489 bool MediaController::isBlocked() const
490 {
491     // A MediaController is a blocked media controller if the MediaController is a paused media 
492     // controller,
493     if (m_paused)
494         return true;
495     
496     if (m_mediaElements.isEmpty())
497         return false;
498     
499     bool allPaused = true;
500     for (auto& element : m_mediaElements) {
501         //  or if any of its slaved media elements are blocked media elements,
502         if (element->isBlocked())
503             return true;
504         
505         // or if any of its slaved media elements whose autoplaying flag is true still have their 
506         // paused attribute set to true,
507         if (element->isAutoplaying() && element->paused())
508             return true;
509         
510         if (!element->paused())
511             allPaused = false;
512     }
513     
514     // or if all of its slaved media elements have their paused attribute set to true.
515     return allPaused;
516 }
517
518 bool MediaController::hasEnded() const
519 {
520     // If the ... media controller playback rate is positive or zero
521     if (m_clock->playRate() < 0)
522         return false;
523
524     // [and] all of the MediaController's slaved media elements have ended playback ... let new
525     // playback state be ended.
526     if (m_mediaElements.isEmpty())
527         return false;
528     
529     bool allHaveEnded = true;
530     for (auto& mediaElement : m_mediaElements) {
531         if (!mediaElement->ended())
532             allHaveEnded = false;
533     }
534     return allHaveEnded;
535 }
536
537 void MediaController::scheduleEvent(const AtomicString& eventName)
538 {
539     m_pendingEvents.append(Event::create(eventName, false, true));
540     if (!m_asyncEventTimer.isActive())
541         m_asyncEventTimer.startOneShot(0);
542 }
543
544 void MediaController::asyncEventTimerFired()
545 {
546     Vector<Ref<Event>> pendingEvents;
547
548     m_pendingEvents.swap(pendingEvents);
549     for (auto& pendingEvent : pendingEvents)
550         dispatchEvent(pendingEvent);
551 }
552
553 void MediaController::clearPositionTimerFired()
554 {
555     m_position = MediaPlayer::invalidTime();
556 }
557
558 bool MediaController::hasAudio() const
559 {
560     for (auto& mediaElement : m_mediaElements) {
561         if (mediaElement->hasAudio())
562             return true;
563     }
564     return false;
565 }
566
567 bool MediaController::hasVideo() const
568 {
569     for (auto& mediaElement : m_mediaElements) {
570         if (mediaElement->hasVideo())
571             return true;
572     }
573     return false;
574 }
575
576 bool MediaController::hasClosedCaptions() const
577 {
578     for (auto& mediaElement : m_mediaElements) {
579         if (mediaElement->hasClosedCaptions())
580             return true;
581     }
582     return false;
583 }
584
585 void MediaController::setClosedCaptionsVisible(bool visible)
586 {
587     m_closedCaptionsVisible = visible;
588     for (auto& mediaElement : m_mediaElements)
589         mediaElement->setClosedCaptionsVisible(visible);
590 }
591
592 bool MediaController::supportsScanning() const
593 {
594     for (auto& mediaElement : m_mediaElements) {
595         if (!mediaElement->supportsScanning())
596             return false;
597     }
598     return true;
599 }
600
601 void MediaController::beginScrubbing()
602 {
603     for (auto& mediaElement : m_mediaElements)
604         mediaElement->beginScrubbing();
605     if (m_playbackState == PLAYING)
606         m_clock->stop();
607 }
608
609 void MediaController::endScrubbing()
610 {
611     for (auto& mediaElement : m_mediaElements)
612         mediaElement->endScrubbing();
613     if (m_playbackState == PLAYING)
614         m_clock->start();
615 }
616
617 void MediaController::beginScanning(ScanDirection direction)
618 {
619     for (auto& mediaElement : m_mediaElements)
620         mediaElement->beginScanning(direction);
621 }
622
623 void MediaController::endScanning()
624 {
625     for (auto& mediaElement : m_mediaElements)
626         mediaElement->endScanning();
627 }
628
629 bool MediaController::canPlay() const
630 {
631     if (m_paused)
632         return true;
633
634     for (auto& mediaElement : m_mediaElements) {
635         if (!mediaElement->canPlay())
636             return false;
637     }
638     return true;
639 }
640
641 bool MediaController::isLiveStream() const
642 {
643     for (auto& mediaElement : m_mediaElements) {
644         if (!mediaElement->isLiveStream())
645             return false;
646     }
647     return true;
648 }
649
650 bool MediaController::hasCurrentSrc() const
651 {
652     for (auto& mediaElement : m_mediaElements) {
653         if (!mediaElement->hasCurrentSrc())
654             return false;
655     }
656     return true;
657 }
658
659 void MediaController::returnToRealtime()
660 {
661     for (auto& mediaElement : m_mediaElements)
662         mediaElement->returnToRealtime();
663 }
664
665 // The spec says to fire periodic timeupdate events (those sent while playing) every
666 // "15 to 250ms", we choose the slowest frequency
667 static const double maxTimeupdateEventFrequency = 0.25;
668
669 void MediaController::startTimeupdateTimer()
670 {
671     if (m_timeupdateTimer.isActive())
672         return;
673
674     m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency);
675 }
676
677 void MediaController::scheduleTimeupdateEvent()
678 {
679     double now = monotonicallyIncreasingTime();
680     double timedelta = now - m_previousTimeupdateTime;
681
682     if (timedelta < maxTimeupdateEventFrequency)
683         return;
684
685     scheduleEvent(eventNames().timeupdateEvent);
686     m_previousTimeupdateTime = now;
687 }
688
689 #endif