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