cf7ac4bcff7324e9e002a9c704f3022f9318f602
[WebKit-https.git] / Source / WebCore / html / shadow / MediaControlsApple.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if ENABLE(VIDEO)
30 #include "MediaControlsApple.h"
31
32 #include "CSSValueKeywords.h"
33 #include "ExceptionCodePlaceholder.h"
34 #include "HTMLNames.h"
35 #include "WheelEvent.h"
36
37 namespace WebCore {
38
39 MediaControlsApple::MediaControlsApple(Document& document)
40     : MediaControls(document)
41     , m_rewindButton(0)
42     , m_returnToRealTimeButton(0)
43     , m_statusDisplay(0)
44     , m_timeRemainingDisplay(0)
45     , m_timelineContainer(0)
46     , m_seekBackButton(0)
47     , m_seekForwardButton(0)
48     , m_closedCaptionsTrackList(0)
49     , m_closedCaptionsContainer(0)
50     , m_volumeSliderMuteButton(0)
51     , m_volumeSliderContainer(0)
52     , m_fullScreenMinVolumeButton(0)
53     , m_fullScreenVolumeSlider(0)
54     , m_fullScreenMaxVolumeButton(0)
55 {
56 }
57
58 RefPtr<MediaControls> MediaControls::tryCreate(Document& document)
59 {
60     return MediaControlsApple::tryCreateControls(document);
61 }
62
63 RefPtr<MediaControlsApple> MediaControlsApple::tryCreateControls(Document& document)
64 {
65     if (!document.page())
66         return nullptr;
67
68     auto controls = adoptRef(*new MediaControlsApple(document));
69
70     auto panel = MediaControlPanelElement::create(document);
71
72     ExceptionCode ec;
73
74     auto rewindButton = MediaControlRewindButtonElement::create(document);
75     controls->m_rewindButton = rewindButton.ptr();
76     panel->appendChild(rewindButton, ec);
77     if (ec)
78         return nullptr;
79
80     auto playButton = MediaControlPlayButtonElement::create(document);
81     controls->m_playButton = playButton.ptr();
82     panel->appendChild(playButton, ec);
83     if (ec)
84         return nullptr;
85
86     auto returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(document);
87     controls->m_returnToRealTimeButton = returnToRealtimeButton.ptr();
88     panel->appendChild(returnToRealtimeButton, ec);
89     if (ec)
90         return nullptr;
91
92     if (document.page()->theme().usesMediaControlStatusDisplay()) {
93         auto statusDisplay = MediaControlStatusDisplayElement::create(document);
94         controls->m_statusDisplay = statusDisplay.ptr();
95         panel->appendChild(statusDisplay, ec);
96         if (ec)
97             return nullptr;
98     }
99
100     auto timelineContainer = MediaControlTimelineContainerElement::create(document);
101
102     auto currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document);
103     controls->m_currentTimeDisplay = currentTimeDisplay.ptr();
104     timelineContainer->appendChild(currentTimeDisplay, ec);
105     if (ec)
106         return nullptr;
107
108     auto timeline = MediaControlTimelineElement::create(document, controls.ptr());
109     controls->m_timeline = timeline.ptr();
110     timelineContainer->appendChild(timeline, ec);
111     if (ec)
112         return nullptr;
113
114     auto timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(document);
115     controls->m_timeRemainingDisplay = timeRemainingDisplay.ptr();
116     timelineContainer->appendChild(timeRemainingDisplay, ec);
117     if (ec)
118         return nullptr;
119
120     controls->m_timelineContainer = timelineContainer.ptr();
121     panel->appendChild(timelineContainer, ec);
122     if (ec)
123         return nullptr;
124
125     // FIXME: Only create when needed <http://webkit.org/b/57163>
126     auto seekBackButton = MediaControlSeekBackButtonElement::create(document);
127     controls->m_seekBackButton = seekBackButton.ptr();
128     panel->appendChild(seekBackButton, ec);
129     if (ec)
130         return nullptr;
131
132     // FIXME: Only create when needed <http://webkit.org/b/57163>
133     auto seekForwardButton = MediaControlSeekForwardButtonElement::create(document);
134     controls->m_seekForwardButton = seekForwardButton.ptr();
135     panel->appendChild(seekForwardButton, ec);
136     if (ec)
137         return nullptr;
138
139     if (document.page()->theme().supportsClosedCaptioning()) {
140         auto closedCaptionsContainer = MediaControlClosedCaptionsContainerElement::create(document);
141
142         auto closedCaptionsTrackList = MediaControlClosedCaptionsTrackListElement::create(document, controls.ptr());
143         controls->m_closedCaptionsTrackList = closedCaptionsTrackList.ptr();
144         closedCaptionsContainer->appendChild(closedCaptionsTrackList, ec);
145         if (ec)
146             return nullptr;
147
148         auto toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, controls.ptr());
149         controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.ptr();
150         panel->appendChild(toggleClosedCaptionsButton, ec);
151         if (ec)
152             return nullptr;
153
154         controls->m_closedCaptionsContainer = closedCaptionsContainer.ptr();
155         controls->appendChild(closedCaptionsContainer, ec);
156         if (ec)
157             return nullptr;
158     }
159
160     // FIXME: Only create when needed <http://webkit.org/b/57163>
161     auto fullScreenButton = MediaControlFullscreenButtonElement::create(document);
162     controls->m_fullScreenButton = fullScreenButton.ptr();
163     panel->appendChild(fullScreenButton, ec);
164
165     // The mute button and the slider element should be in the same div.
166     auto panelVolumeControlContainer = HTMLDivElement::create(document);
167
168     if (document.page()->theme().usesMediaControlVolumeSlider()) {
169         auto volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(document);
170
171         auto slider = MediaControlPanelVolumeSliderElement::create(document);
172         controls->m_volumeSlider = slider.ptr();
173         volumeSliderContainer->appendChild(slider, ec);
174         if (ec)
175             return nullptr;
176
177         // This is a duplicate mute button, which is visible in some ports at the bottom of the volume bar.
178         // It's important only when the volume bar is displayed below the controls.
179         auto volumeSliderMuteButton = MediaControlVolumeSliderMuteButtonElement::create(document);
180         controls->m_volumeSliderMuteButton = volumeSliderMuteButton.ptr();
181         volumeSliderContainer->appendChild(volumeSliderMuteButton, ec);
182
183         if (ec)
184             return nullptr;
185
186         controls->m_volumeSliderContainer = volumeSliderContainer.ptr();
187         panelVolumeControlContainer->appendChild(volumeSliderContainer, ec);
188         if (ec)
189             return nullptr;
190     }
191
192     auto panelMuteButton = MediaControlPanelMuteButtonElement::create(document, controls.ptr());
193     controls->m_panelMuteButton = panelMuteButton.ptr();
194     panelVolumeControlContainer->appendChild(panelMuteButton, ec);
195     if (ec)
196         return nullptr;
197
198     panel->appendChild(panelVolumeControlContainer, ec);
199     if (ec)
200         return nullptr;
201
202     // FIXME: Only create when needed <http://webkit.org/b/57163>
203     auto fullScreenMinVolumeButton = MediaControlFullscreenVolumeMinButtonElement::create(document);
204     controls->m_fullScreenMinVolumeButton = fullScreenMinVolumeButton.ptr();
205     panel->appendChild(fullScreenMinVolumeButton, ec);
206     if (ec)
207         return nullptr;
208
209     auto fullScreenVolumeSlider = MediaControlFullscreenVolumeSliderElement::create(document);
210     controls->m_fullScreenVolumeSlider = fullScreenVolumeSlider.ptr();
211     panel->appendChild(fullScreenVolumeSlider, ec);
212     if (ec)
213         return nullptr;
214
215     auto fullScreenMaxVolumeButton = MediaControlFullscreenVolumeMaxButtonElement::create(document);
216     controls->m_fullScreenMaxVolumeButton = fullScreenMaxVolumeButton.ptr();
217     panel->appendChild(fullScreenMaxVolumeButton, ec);
218     if (ec)
219         return nullptr;
220
221     controls->m_panel = panel.ptr();
222     controls->appendChild(panel, ec);
223     if (ec)
224         return nullptr;
225
226     return WTFMove(controls);
227 }
228
229 void MediaControlsApple::setMediaController(MediaControllerInterface* controller)
230 {
231     if (m_mediaController == controller)
232         return;
233
234     MediaControls::setMediaController(controller);
235
236     if (m_rewindButton)
237         m_rewindButton->setMediaController(controller);
238     if (m_returnToRealTimeButton)
239         m_returnToRealTimeButton->setMediaController(controller);
240     if (m_statusDisplay)
241         m_statusDisplay->setMediaController(controller);
242     if (m_timeRemainingDisplay)
243         m_timeRemainingDisplay->setMediaController(controller);
244     if (m_timelineContainer)
245         m_timelineContainer->setMediaController(controller);
246     if (m_seekBackButton)
247         m_seekBackButton->setMediaController(controller);
248     if (m_seekForwardButton)
249         m_seekForwardButton->setMediaController(controller);
250     if (m_volumeSliderMuteButton)
251         m_volumeSliderMuteButton->setMediaController(controller);
252     if (m_volumeSliderContainer)
253         m_volumeSliderContainer->setMediaController(controller);
254     if (m_fullScreenMinVolumeButton)
255         m_fullScreenMinVolumeButton->setMediaController(controller);
256     if (m_fullScreenVolumeSlider)
257         m_fullScreenVolumeSlider->setMediaController(controller);
258     if (m_fullScreenMaxVolumeButton)
259         m_fullScreenMaxVolumeButton->setMediaController(controller);
260     if (m_closedCaptionsTrackList)
261         m_closedCaptionsTrackList->setMediaController(controller);
262     if (m_closedCaptionsContainer)
263         m_closedCaptionsContainer->setMediaController(controller);
264 }
265
266 void MediaControlsApple::defaultEventHandler(Event* event)
267 {
268     if (event->type() == eventNames().clickEvent) {
269         if (m_closedCaptionsContainer && m_closedCaptionsContainer->isShowing()) {
270             hideClosedCaptionTrackList();
271             event->setDefaultHandled();
272         }
273     }
274
275     MediaControls::defaultEventHandler(event);
276 }
277
278 void MediaControlsApple::hide()
279 {
280     MediaControls::hide();
281     m_volumeSliderContainer->hide();
282     if (m_closedCaptionsContainer)
283         hideClosedCaptionTrackList();
284 }
285
286 void MediaControlsApple::makeTransparent()
287 {
288     MediaControls::makeTransparent();
289     m_volumeSliderContainer->hide();
290     if (m_closedCaptionsContainer)
291         hideClosedCaptionTrackList();
292 }
293
294 void MediaControlsApple::changedClosedCaptionsVisibility()
295 {
296     MediaControls::changedClosedCaptionsVisibility();
297     if (m_closedCaptionsContainer && m_closedCaptionsContainer->isShowing())
298         hideClosedCaptionTrackList();
299
300 }
301
302 void MediaControlsApple::reset()
303 {
304     Page* page = document().page();
305     if (!page)
306         return;
307
308     updateStatusDisplay();
309
310     if (m_mediaController->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard))
311         m_fullScreenButton->show();
312     else
313         m_fullScreenButton->hide();
314
315     double duration = m_mediaController->duration();
316     if (std::isfinite(duration) || page->theme().hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
317         m_timeline->setDuration(duration);
318         m_timelineContainer->show();
319         m_timeline->setPosition(m_mediaController->currentTime());
320         updateCurrentTimeDisplay();
321     } else
322         m_timelineContainer->hide();
323
324     if (m_mediaController->hasAudio() || page->theme().hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
325         m_panelMuteButton->show();
326     else
327         m_panelMuteButton->hide();
328
329     if (m_volumeSlider)
330         setSliderVolume();
331
332     if (m_toggleClosedCaptionsButton) {
333         if (m_mediaController->hasClosedCaptions())
334             m_toggleClosedCaptionsButton->show();
335         else
336             m_toggleClosedCaptionsButton->hide();
337     }
338
339     if (m_playButton)
340         m_playButton->updateDisplayType();
341
342 #if ENABLE(FULLSCREEN_API)
343     if (m_fullScreenVolumeSlider)
344         setFullscreenSliderVolume();
345
346     if (m_isFullscreen) {
347         if (m_mediaController->isLiveStream()) {
348             m_seekBackButton->hide();
349             m_seekForwardButton->hide();
350             m_rewindButton->show();
351             m_returnToRealTimeButton->show();
352         } else {
353             m_seekBackButton->show();
354             m_seekForwardButton->show();
355             m_rewindButton->hide();
356             m_returnToRealTimeButton->hide();
357         }
358     } else
359 #endif
360     if (!m_mediaController->isLiveStream()) {
361         m_returnToRealTimeButton->hide();
362         m_rewindButton->show();
363     } else {
364         m_returnToRealTimeButton->show();
365         m_rewindButton->hide();
366     }
367
368     makeOpaque();
369 }
370
371 void MediaControlsApple::updateCurrentTimeDisplay()
372 {
373     double now = m_mediaController->currentTime();
374     double duration = m_mediaController->duration();
375
376     Page* page = document().page();
377     if (!page)
378         return;
379
380     // Allow the theme to format the time.
381     m_currentTimeDisplay->setInnerText(page->theme().formatMediaControlsCurrentTime(now, duration), IGNORE_EXCEPTION);
382     m_currentTimeDisplay->setCurrentValue(now);
383     m_timeRemainingDisplay->setInnerText(page->theme().formatMediaControlsRemainingTime(now, duration), IGNORE_EXCEPTION);
384     m_timeRemainingDisplay->setCurrentValue(now - duration);
385 }
386
387 void MediaControlsApple::reportedError()
388 {
389     Page* page = document().page();
390     if (!page)
391         return;
392
393     if (!page->theme().hasOwnDisabledStateHandlingFor(MediaSliderPart))
394         m_timelineContainer->hide();
395
396     if (!page->theme().hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
397         m_panelMuteButton->hide();
398
399     m_fullScreenButton->hide();
400
401     if (m_volumeSliderContainer)
402         m_volumeSliderContainer->hide();
403     if (m_toggleClosedCaptionsButton && !page->theme().hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
404         m_toggleClosedCaptionsButton->hide();
405     if (m_closedCaptionsContainer)
406         hideClosedCaptionTrackList();
407 }
408
409 void MediaControlsApple::updateStatusDisplay()
410 {
411     if (m_statusDisplay)
412         m_statusDisplay->update();
413 }
414
415 void MediaControlsApple::loadedMetadata()
416 {
417     if (m_statusDisplay && !m_mediaController->isLiveStream())
418         m_statusDisplay->hide();
419
420     MediaControls::loadedMetadata();
421 }
422
423 void MediaControlsApple::changedMute()
424 {
425     MediaControls::changedMute();
426
427     if (m_volumeSliderMuteButton)
428         m_volumeSliderMuteButton->changedMute();
429 }
430
431 void MediaControlsApple::changedVolume()
432 {
433     MediaControls::changedVolume();
434
435     if (m_fullScreenVolumeSlider)
436         setFullscreenSliderVolume();
437 }
438
439 void MediaControlsApple::enteredFullscreen()
440 {
441     MediaControls::enteredFullscreen();
442     m_panel->setCanBeDragged(true);
443
444     if (m_mediaController->isLiveStream()) {
445         m_seekBackButton->hide();
446         m_seekForwardButton->hide();
447         m_rewindButton->show();
448         m_returnToRealTimeButton->show();
449     } else {
450         m_seekBackButton->show();
451         m_seekForwardButton->show();
452         m_rewindButton->hide();
453         m_returnToRealTimeButton->hide();
454     }
455 }
456
457 void MediaControlsApple::exitedFullscreen()
458 {
459     m_rewindButton->show();
460     m_seekBackButton->show();
461     m_seekForwardButton->show();
462     m_returnToRealTimeButton->show();
463
464     m_panel->setCanBeDragged(false);
465
466     // We will keep using the panel, but we want it to go back to the standard position.
467     // This will matter right away because we use the panel even when not fullscreen.
468     // And if we reenter fullscreen we also want the panel in the standard position.
469     m_panel->resetPosition();
470
471     MediaControls::exitedFullscreen();
472 }
473
474 void MediaControlsApple::showVolumeSlider()
475 {
476     if (!m_mediaController->hasAudio())
477         return;
478
479     if (m_volumeSliderContainer)
480         m_volumeSliderContainer->show();
481 }
482
483 void MediaControlsApple::toggleClosedCaptionTrackList()
484 {
485     if (!m_mediaController->hasClosedCaptions())
486         return;
487
488     if (m_closedCaptionsContainer) {
489         if (m_closedCaptionsContainer->isShowing())
490             hideClosedCaptionTrackList();
491         else {
492             if (m_closedCaptionsTrackList)
493                 m_closedCaptionsTrackList->updateDisplay();
494             showClosedCaptionTrackList();
495         }
496     }
497 }
498
499 void MediaControlsApple::showClosedCaptionTrackList()
500 {
501     if (!m_closedCaptionsContainer || m_closedCaptionsContainer->isShowing())
502         return;
503
504     m_closedCaptionsContainer->show();
505
506     // Ensure the controls panel does not receive any events while the captions
507     // track list is visible as all events now need to be captured by the
508     // track list.
509     m_panel->setInlineStyleProperty(CSSPropertyPointerEvents, CSSValueNone);
510
511     EventListener& listener = eventListener();
512     m_closedCaptionsContainer->addEventListener(eventNames().wheelEvent, &listener, true);
513
514     // Track click events in the capture phase at two levels, first at the document level
515     // such that a click outside of the <video> may dismiss the track list, second at the
516     // media controls level such that a click anywhere outside of the track list hides the
517     // track list. These two levels are necessary since it would not be possible to get a
518     // reference to the track list when handling the event outside of the shadow tree.
519     document().addEventListener(eventNames().clickEvent, &listener, true);
520     addEventListener(eventNames().clickEvent, &listener, true);
521 }
522
523 void MediaControlsApple::hideClosedCaptionTrackList()
524 {
525     if (!m_closedCaptionsContainer || !m_closedCaptionsContainer->isShowing())
526         return;
527
528     m_closedCaptionsContainer->hide();
529
530     // Buttons in the controls panel may now be interactive.
531     m_panel->removeInlineStyleProperty(CSSPropertyPointerEvents);
532
533     EventListener& listener = eventListener();
534     m_closedCaptionsContainer->removeEventListener(eventNames().wheelEvent, &listener, true);
535     document().removeEventListener(eventNames().clickEvent, &listener, true);
536     removeEventListener(eventNames().clickEvent, &listener, true);
537 }
538
539 void MediaControlsApple::setFullscreenSliderVolume()
540 {
541     m_fullScreenVolumeSlider->setVolume(m_mediaController->muted() ? 0.0 : m_mediaController->volume());
542 }
543
544 bool MediaControlsApple::shouldClosedCaptionsContainerPreventPageScrolling(int wheelDeltaY)
545 {
546     int scrollTop = m_closedCaptionsContainer->scrollTop();
547     // Scrolling down.
548     if (wheelDeltaY < 0 && (scrollTop + m_closedCaptionsContainer->offsetHeight()) >= m_closedCaptionsContainer->scrollHeight())
549         return true;
550     // Scrolling up.
551     if (wheelDeltaY > 0 && scrollTop <= 0)
552         return true;
553     return false;
554 }
555
556 void MediaControlsApple::handleClickEvent(Event* event)
557 {
558     Node* currentTarget = event->currentTarget()->toNode();
559     Node* target = event->target()->toNode();
560
561     if ((currentTarget == &document() && !shadowHost()->contains(target)) || (currentTarget == this && !m_closedCaptionsContainer->contains(target))) {
562         hideClosedCaptionTrackList();
563         event->stopImmediatePropagation();
564         event->setDefaultHandled();
565     }
566 }
567
568 void MediaControlsApple::closedCaptionTracksChanged()
569 {
570     if (m_toggleClosedCaptionsButton) {
571         if (m_mediaController->hasClosedCaptions())
572             m_toggleClosedCaptionsButton->show();
573         else
574             m_toggleClosedCaptionsButton->hide();
575     }
576 }
577
578 MediaControlsAppleEventListener& MediaControlsApple::eventListener()
579 {
580     if (!m_eventListener)
581         m_eventListener = MediaControlsAppleEventListener::create(this);
582     return *m_eventListener;
583 }
584
585 // --------
586
587 void MediaControlsAppleEventListener::handleEvent(ScriptExecutionContext*, Event* event)
588 {
589     if (event->type() == eventNames().clickEvent)
590         m_mediaControls->handleClickEvent(event);
591     else if (eventNames().isWheelEventType(event->type()) && is<WheelEvent>(*event)) {
592         WheelEvent& wheelEvent = downcast<WheelEvent>(*event);
593         if (m_mediaControls->shouldClosedCaptionsContainerPreventPageScrolling(wheelEvent.wheelDeltaY()))
594             wheelEvent.preventDefault();
595     }
596 }
597
598 bool MediaControlsAppleEventListener::operator==(const EventListener& listener)
599 {
600     if (const MediaControlsAppleEventListener* mediaControlsAppleEventListener = MediaControlsAppleEventListener::cast(&listener))
601         return m_mediaControls == mediaControlsAppleEventListener->m_mediaControls;
602     return false;
603 }
604
605 }
606
607 #endif