1/*
2 * Copyright (C) 2011, 2012 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 "MediaControls.h"
31
32#include "EventNames.h"
33#include "MouseEvent.h"
34#include "Page.h"
35#include "RenderElement.h"
36#include "RenderTheme.h"
37#include "Settings.h"
38#include <wtf/IsoMallocInlines.h>
39
40namespace WebCore {
41
42WTF_MAKE_ISO_ALLOCATED_IMPL(MediaControls);
43
44MediaControls::MediaControls(Document& document)
45 : HTMLDivElement(HTMLNames::divTag, document)
46 , m_mediaController(0)
47 , m_panel(0)
48#if ENABLE(VIDEO_TRACK)
49 , m_textDisplayContainer(0)
50#endif
51 , m_playButton(0)
52 , m_currentTimeDisplay(0)
53 , m_timeline(0)
54 , m_panelMuteButton(0)
55 , m_volumeSlider(0)
56 , m_toggleClosedCaptionsButton(0)
57 , m_fullScreenButton(0)
58 , m_hideFullscreenControlsTimer(*this, &MediaControls::hideFullscreenControlsTimerFired)
59 , m_isFullscreen(false)
60 , m_isMouseOverControls(false)
61{
62 setPseudo(AtomicString("-webkit-media-controls", AtomicString::ConstructFromLiteral));
63}
64
65void MediaControls::setMediaController(MediaControllerInterface* controller)
66{
67 if (m_mediaController == controller)
68 return;
69 m_mediaController = controller;
70
71 if (m_panel)
72 m_panel->setMediaController(controller);
73#if ENABLE(VIDEO_TRACK)
74 if (m_textDisplayContainer)
75 m_textDisplayContainer->setMediaController(controller);
76#endif
77 if (m_playButton)
78 m_playButton->setMediaController(controller);
79 if (m_currentTimeDisplay)
80 m_currentTimeDisplay->setMediaController(controller);
81 if (m_timeline)
82 m_timeline->setMediaController(controller);
83 if (m_panelMuteButton)
84 m_panelMuteButton->setMediaController(controller);
85 if (m_volumeSlider)
86 m_volumeSlider->setMediaController(controller);
87 if (m_toggleClosedCaptionsButton)
88 m_toggleClosedCaptionsButton->setMediaController(controller);
89 if (m_fullScreenButton)
90 m_fullScreenButton->setMediaController(controller);
91}
92
93void MediaControls::reset()
94{
95 m_playButton->updateDisplayType();
96
97 updateCurrentTimeDisplay();
98
99 double duration = m_mediaController->duration();
100 if (std::isfinite(duration) || RenderTheme::singleton().hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
101 m_timeline->setDuration(duration);
102 m_timeline->setPosition(m_mediaController->currentTime());
103 }
104
105 if (m_mediaController->hasAudio() || RenderTheme::singleton().hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
106 m_panelMuteButton->show();
107 else
108 m_panelMuteButton->hide();
109
110 if (m_volumeSlider) {
111 if (!m_mediaController->hasAudio())
112 m_volumeSlider->hide();
113 else {
114 m_volumeSlider->show();
115 setSliderVolume();
116 }
117 }
118
119 refreshClosedCaptionsButtonVisibility();
120
121 if (m_fullScreenButton) {
122 if (m_mediaController->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard) && m_mediaController->hasVideo())
123 m_fullScreenButton->show();
124 else
125 m_fullScreenButton->hide();
126 }
127
128 makeOpaque();
129}
130
131void MediaControls::reportedError()
132{
133 auto& theme = RenderTheme::singleton();
134 if (!theme.hasOwnDisabledStateHandlingFor(MediaMuteButtonPart)) {
135 m_panelMuteButton->hide();
136 m_volumeSlider->hide();
137 }
138
139 if (m_toggleClosedCaptionsButton && !theme.hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
140 m_toggleClosedCaptionsButton->hide();
141
142 if (m_fullScreenButton && !theme.hasOwnDisabledStateHandlingFor(MediaEnterFullscreenButtonPart))
143 m_fullScreenButton->hide();
144}
145
146void MediaControls::loadedMetadata()
147{
148 reset();
149}
150
151void MediaControls::show()
152{
153 makeOpaque();
154 m_panel->setIsDisplayed(true);
155 m_panel->show();
156}
157
158void MediaControls::hide()
159{
160 m_panel->setIsDisplayed(false);
161 m_panel->hide();
162}
163
164void MediaControls::makeOpaque()
165{
166 m_panel->makeOpaque();
167}
168
169void MediaControls::makeTransparent()
170{
171 m_panel->makeTransparent();
172}
173
174bool MediaControls::shouldHideControls()
175{
176 return !m_panel->hovered();
177}
178
179void MediaControls::bufferingProgressed()
180{
181 // We only need to update buffering progress when paused, during normal
182 // playback playbackProgressed() will take care of it.
183 if (m_mediaController->paused())
184 m_timeline->setPosition(m_mediaController->currentTime());
185}
186
187void MediaControls::playbackStarted()
188{
189 m_playButton->updateDisplayType();
190 m_timeline->setPosition(m_mediaController->currentTime());
191 updateCurrentTimeDisplay();
192
193 if (m_isFullscreen)
194 startHideFullscreenControlsTimer();
195}
196
197void MediaControls::playbackProgressed()
198{
199 m_timeline->setPosition(m_mediaController->currentTime());
200 updateCurrentTimeDisplay();
201
202 if (!m_isMouseOverControls && m_mediaController->hasVideo())
203 makeTransparent();
204}
205
206void MediaControls::playbackStopped()
207{
208 m_playButton->updateDisplayType();
209 m_timeline->setPosition(m_mediaController->currentTime());
210 updateCurrentTimeDisplay();
211 makeOpaque();
212
213 stopHideFullscreenControlsTimer();
214}
215
216void MediaControls::updateCurrentTimeDisplay()
217{
218 double now = m_mediaController->currentTime();
219 m_currentTimeDisplay->setInnerText(RenderTheme::singleton().formatMediaControlsTime(now));
220 m_currentTimeDisplay->setCurrentValue(now);
221}
222
223void MediaControls::showVolumeSlider()
224{
225 if (!m_mediaController->hasAudio())
226 return;
227
228 m_volumeSlider->show();
229}
230
231void MediaControls::changedMute()
232{
233 m_panelMuteButton->changedMute();
234}
235
236void MediaControls::changedVolume()
237{
238 if (m_volumeSlider)
239 setSliderVolume();
240 if (m_panelMuteButton && m_panelMuteButton->renderer())
241 m_panelMuteButton->renderer()->repaint();
242}
243
244void MediaControls::changedClosedCaptionsVisibility()
245{
246 if (m_toggleClosedCaptionsButton)
247 m_toggleClosedCaptionsButton->updateDisplayType();
248}
249
250void MediaControls::refreshClosedCaptionsButtonVisibility()
251{
252 if (!m_toggleClosedCaptionsButton)
253 return;
254
255 if (m_mediaController->hasClosedCaptions())
256 m_toggleClosedCaptionsButton->show();
257 else
258 m_toggleClosedCaptionsButton->hide();
259}
260
261void MediaControls::closedCaptionTracksChanged()
262{
263 refreshClosedCaptionsButtonVisibility();
264}
265
266void MediaControls::enteredFullscreen()
267{
268 m_isFullscreen = true;
269 m_fullScreenButton->setIsFullscreen(true);
270
271 if (Page* page = document().page())
272 page->chrome().setCursorHiddenUntilMouseMoves(true);
273
274 startHideFullscreenControlsTimer();
275#if ENABLE(VIDEO_TRACK)
276 if (m_textDisplayContainer)
277 m_textDisplayContainer->enteredFullscreen();
278#endif
279}
280
281void MediaControls::exitedFullscreen()
282{
283 m_isFullscreen = false;
284 m_fullScreenButton->setIsFullscreen(false);
285 stopHideFullscreenControlsTimer();
286#if ENABLE(VIDEO_TRACK)
287 if (m_textDisplayContainer)
288 m_textDisplayContainer->exitedFullscreen();
289#endif
290}
291
292void MediaControls::defaultEventHandler(Event& event)
293{
294 HTMLDivElement::defaultEventHandler(event);
295
296 if (event.type() == eventNames().mouseoverEvent) {
297 if (!containsRelatedTarget(event)) {
298 m_isMouseOverControls = true;
299 if (!m_mediaController->canPlay()) {
300 makeOpaque();
301 if (shouldHideControls())
302 startHideFullscreenControlsTimer();
303 }
304 }
305 return;
306 }
307
308 if (event.type() == eventNames().mouseoutEvent) {
309 if (!containsRelatedTarget(event)) {
310 m_isMouseOverControls = false;
311 stopHideFullscreenControlsTimer();
312 }
313 return;
314 }
315
316 if (event.type() == eventNames().mousemoveEvent) {
317 if (m_isFullscreen) {
318 // When we get a mouse move in fullscreen mode, show the media controls, and start a timer
319 // that will hide the media controls after a 3 seconds without a mouse move.
320 makeOpaque();
321 if (shouldHideControls())
322 startHideFullscreenControlsTimer();
323 }
324 return;
325 }
326}
327
328void MediaControls::hideFullscreenControlsTimerFired()
329{
330 if (m_mediaController->paused())
331 return;
332
333 if (!m_isFullscreen)
334 return;
335
336 if (!shouldHideControls())
337 return;
338
339 if (Page* page = document().page())
340 page->chrome().setCursorHiddenUntilMouseMoves(true);
341
342 makeTransparent();
343}
344
345void MediaControls::startHideFullscreenControlsTimer()
346{
347 if (!m_isFullscreen)
348 return;
349
350 Page* page = document().page();
351 if (!page)
352 return;
353
354 m_hideFullscreenControlsTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls());
355}
356
357void MediaControls::stopHideFullscreenControlsTimer()
358{
359 m_hideFullscreenControlsTimer.stop();
360}
361
362bool MediaControls::containsRelatedTarget(Event& event)
363{
364 if (!is<MouseEvent>(event))
365 return false;
366 auto relatedTarget = downcast<MouseEvent>(event).relatedTarget();
367 return is<Node>(relatedTarget) && contains(&downcast<Node>(*relatedTarget));
368}
369
370#if ENABLE(VIDEO_TRACK)
371
372void MediaControls::createTextTrackDisplay()
373{
374 if (m_textDisplayContainer)
375 return;
376
377 auto textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
378 m_textDisplayContainer = textDisplayContainer.ptr();
379
380 if (m_mediaController)
381 m_textDisplayContainer->setMediaController(m_mediaController);
382
383 // Insert it before the first controller element so it always displays behind the controls.
384 insertBefore(textDisplayContainer, m_panel);
385}
386
387void MediaControls::showTextTrackDisplay()
388{
389 if (!m_textDisplayContainer)
390 createTextTrackDisplay();
391 m_textDisplayContainer->show();
392}
393
394void MediaControls::hideTextTrackDisplay()
395{
396 if (!m_textDisplayContainer)
397 createTextTrackDisplay();
398 m_textDisplayContainer->hide();
399}
400
401void MediaControls::updateTextTrackDisplay()
402{
403 if (!m_textDisplayContainer)
404 createTextTrackDisplay();
405
406 m_textDisplayContainer->updateDisplay();
407}
408
409void MediaControls::textTrackPreferencesChanged()
410{
411 closedCaptionTracksChanged();
412 if (m_textDisplayContainer)
413 m_textDisplayContainer->updateSizes(true);
414}
415
416void MediaControls::clearTextDisplayContainer()
417{
418 if (m_textDisplayContainer)
419 m_textDisplayContainer->removeChildren();
420}
421
422#endif
423
424void MediaControls::setSliderVolume()
425{
426 m_volumeSlider->setVolume(m_mediaController->muted() ? 0.0 : m_mediaController->volume());
427}
428
429}
430
431#endif
432