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