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