1 /*
2 * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 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
29 #if ENABLE(VIDEO)
30 #include "MediaControlRootElement.h"
31
32 #include "MediaControlElements.h"
33 #include "Page.h"
34 #include "RenderTheme.h"
35
36 using namespace std;
37
38 namespace WebCore {
39
MediaControlRootElement(HTMLMediaElement * mediaElement)40 MediaControlRootElement::MediaControlRootElement(HTMLMediaElement* mediaElement)
41 : MediaControls(mediaElement)
42 , m_mediaElement(mediaElement)
43 , m_rewindButton(0)
44 , m_playButton(0)
45 , m_returnToRealTimeButton(0)
46 , m_statusDisplay(0)
47 , m_currentTimeDisplay(0)
48 , m_timeline(0)
49 , m_timeRemainingDisplay(0)
50 , m_timelineContainer(0)
51 , m_seekBackButton(0)
52 , m_seekForwardButton(0)
53 , m_toggleClosedCaptionsButton(0)
54 , m_panelMuteButton(0)
55 , m_volumeSlider(0)
56 , m_volumeSliderMuteButton(0)
57 , m_volumeSliderContainer(0)
58 , m_fullScreenButton(0)
59 , m_fullScreenMinVolumeButton(0)
60 , m_fullScreenVolumeSlider(0)
61 , m_fullScreenMaxVolumeButton(0)
62 , m_panel(0)
63 , m_opaque(true)
64 {
65 }
66
create(HTMLMediaElement * mediaElement)67 PassRefPtr<MediaControls> MediaControls::create(HTMLMediaElement* mediaElement)
68 {
69 return MediaControlRootElement::create(mediaElement);
70 }
71
create(HTMLMediaElement * mediaElement)72 PassRefPtr<MediaControlRootElement> MediaControlRootElement::create(HTMLMediaElement* mediaElement)
73 {
74 if (!mediaElement->document()->page())
75 return 0;
76
77 RefPtr<MediaControlRootElement> controls = adoptRef(new MediaControlRootElement(mediaElement));
78
79 RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(mediaElement);
80
81 ExceptionCode ec;
82
83 RefPtr<MediaControlRewindButtonElement> rewindButton = MediaControlRewindButtonElement::create(mediaElement);
84 controls->m_rewindButton = rewindButton.get();
85 panel->appendChild(rewindButton.release(), ec, true);
86 if (ec)
87 return 0;
88
89 RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(mediaElement);
90 controls->m_playButton = playButton.get();
91 panel->appendChild(playButton.release(), ec, true);
92 if (ec)
93 return 0;
94
95 RefPtr<MediaControlReturnToRealtimeButtonElement> returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(mediaElement);
96 controls->m_returnToRealTimeButton = returnToRealtimeButton.get();
97 panel->appendChild(returnToRealtimeButton.release(), ec, true);
98 if (ec)
99 return 0;
100
101 if (mediaElement->document()->page()->theme()->usesMediaControlStatusDisplay()) {
102 RefPtr<MediaControlStatusDisplayElement> statusDisplay = MediaControlStatusDisplayElement::create(mediaElement);
103 controls->m_statusDisplay = statusDisplay.get();
104 panel->appendChild(statusDisplay.release(), ec, true);
105 if (ec)
106 return 0;
107 }
108
109 RefPtr<MediaControlTimelineContainerElement> timelineContainer = MediaControlTimelineContainerElement::create(mediaElement);
110
111 RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(mediaElement);
112 controls->m_currentTimeDisplay = currentTimeDisplay.get();
113 timelineContainer->appendChild(currentTimeDisplay.release(), ec, true);
114 if (ec)
115 return 0;
116
117 RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(mediaElement, controls.get());
118 controls->m_timeline = timeline.get();
119 timelineContainer->appendChild(timeline.release(), ec, true);
120 if (ec)
121 return 0;
122
123 RefPtr<MediaControlTimeRemainingDisplayElement> timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(mediaElement);
124 controls->m_timeRemainingDisplay = timeRemainingDisplay.get();
125 timelineContainer->appendChild(timeRemainingDisplay.release(), ec, true);
126 if (ec)
127 return 0;
128
129 controls->m_timelineContainer = timelineContainer.get();
130 panel->appendChild(timelineContainer.release(), ec, true);
131 if (ec)
132 return 0;
133
134 #if !PLATFORM(ANDROID)
135 // FIXME: Only create when needed <http://webkit.org/b/57163>
136 RefPtr<MediaControlSeekBackButtonElement> seekBackButton = MediaControlSeekBackButtonElement::create(mediaElement);
137 controls->m_seekBackButton = seekBackButton.get();
138 panel->appendChild(seekBackButton.release(), ec, true);
139 if (ec)
140 return 0;
141
142 // FIXME: Only create when needed <http://webkit.org/b/57163>
143 RefPtr<MediaControlSeekForwardButtonElement> seekForwardButton = MediaControlSeekForwardButtonElement::create(mediaElement);
144 controls->m_seekForwardButton = seekForwardButton.get();
145 panel->appendChild(seekForwardButton.release(), ec, true);
146 if (ec)
147 return 0;
148 #endif
149
150 if (mediaElement->document()->page()->theme()->supportsClosedCaptioning()) {
151 RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(mediaElement);
152 controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
153 panel->appendChild(toggleClosedCaptionsButton.release(), ec, true);
154 if (ec)
155 return 0;
156 }
157
158 // FIXME: Only create when needed <http://webkit.org/b/57163>
159 RefPtr<MediaControlFullscreenButtonElement> fullScreenButton = MediaControlFullscreenButtonElement::create(mediaElement, controls.get());
160 controls->m_fullScreenButton = fullScreenButton.get();
161 panel->appendChild(fullScreenButton.release(), ec, true);
162
163 RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(mediaElement, controls.get());
164 controls->m_panelMuteButton = panelMuteButton.get();
165 panel->appendChild(panelMuteButton.release(), ec, true);
166 if (ec)
167 return 0;
168
169 if (mediaElement->document()->page()->theme()->usesMediaControlVolumeSlider()) {
170 RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(mediaElement);
171
172 RefPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(mediaElement);
173 controls->m_volumeSlider = slider.get();
174 volumeSliderContainer->appendChild(slider.release(), ec, true);
175 if (ec)
176 return 0;
177
178 RefPtr<MediaControlVolumeSliderMuteButtonElement> volumeSliderMuteButton = MediaControlVolumeSliderMuteButtonElement::create(mediaElement);
179 controls->m_volumeSliderMuteButton = volumeSliderMuteButton.get();
180 volumeSliderContainer->appendChild(volumeSliderMuteButton.release(), ec, true);
181 if (ec)
182 return 0;
183
184 controls->m_volumeSliderContainer = volumeSliderContainer.get();
185 panel->appendChild(volumeSliderContainer.release(), ec, true);
186 if (ec)
187 return 0;
188 }
189
190 // FIXME: Only create when needed <http://webkit.org/b/57163>
191 RefPtr<MediaControlFullscreenVolumeMinButtonElement> fullScreenMinVolumeButton = MediaControlFullscreenVolumeMinButtonElement::create(mediaElement);
192 controls->m_fullScreenMinVolumeButton = fullScreenMinVolumeButton.get();
193 panel->appendChild(fullScreenMinVolumeButton.release(), ec, true);
194 if (ec)
195 return 0;
196
197 RefPtr<MediaControlFullscreenVolumeSliderElement> fullScreenVolumeSlider = MediaControlFullscreenVolumeSliderElement::create(mediaElement);
198 controls->m_fullScreenVolumeSlider = fullScreenVolumeSlider.get();
199 panel->appendChild(fullScreenVolumeSlider.release(), ec, true);
200 if (ec)
201 return 0;
202
203 RefPtr<MediaControlFullscreenVolumeMaxButtonElement> fullScreenMaxVolumeButton = MediaControlFullscreenVolumeMaxButtonElement::create(mediaElement);
204 controls->m_fullScreenMaxVolumeButton = fullScreenMaxVolumeButton.get();
205 panel->appendChild(fullScreenMaxVolumeButton.release(), ec, true);
206 if (ec)
207 return 0;
208
209 controls->m_panel = panel.get();
210 controls->appendChild(panel.release(), ec, true);
211 if (ec)
212 return 0;
213
214 return controls.release();
215 }
216
show()217 void MediaControlRootElement::show()
218 {
219 m_panel->show();
220 }
221
hide()222 void MediaControlRootElement::hide()
223 {
224 m_panel->hide();
225 }
226
webkitTransitionString()227 static const String& webkitTransitionString()
228 {
229 DEFINE_STATIC_LOCAL(String, s, ("-webkit-transition"));
230 return s;
231 }
232
opacityString()233 static const String& opacityString()
234 {
235 DEFINE_STATIC_LOCAL(String, s, ("opacity"));
236 return s;
237 }
238
makeOpaque()239 void MediaControlRootElement::makeOpaque()
240 {
241 if (m_opaque)
242 return;
243
244 DEFINE_STATIC_LOCAL(String, transitionValue, ());
245 if (transitionValue.isNull())
246 transitionValue = String::format("opacity %.1gs", document()->page()->theme()->mediaControlsFadeInDuration());
247 DEFINE_STATIC_LOCAL(String, opacityValue, ("1"));
248
249 ExceptionCode ec;
250 // FIXME: Make more efficient <http://webkit.org/b/58157>
251 m_panel->style()->setProperty(webkitTransitionString(), transitionValue, ec);
252 m_panel->style()->setProperty(opacityString(), opacityValue, ec);
253 m_opaque = true;
254 }
255
makeTransparent()256 void MediaControlRootElement::makeTransparent()
257 {
258 if (!m_opaque)
259 return;
260
261 DEFINE_STATIC_LOCAL(String, transitionValue, ());
262 if (transitionValue.isNull())
263 transitionValue = String::format("opacity %.1gs", document()->page()->theme()->mediaControlsFadeOutDuration());
264 DEFINE_STATIC_LOCAL(String, opacityValue, ("0"));
265
266 ExceptionCode ec;
267 // FIXME: Make more efficient <http://webkit.org/b/58157>
268 m_panel->style()->setProperty(webkitTransitionString(), transitionValue, ec);
269 m_panel->style()->setProperty(opacityString(), opacityValue, ec);
270 m_opaque = false;
271 }
272
reset()273 void MediaControlRootElement::reset()
274 {
275 Page* page = document()->page();
276 if (!page)
277 return;
278
279 changedNetworkState();
280
281 if (m_mediaElement->supportsFullscreen())
282 m_fullScreenButton->show();
283 else
284 m_fullScreenButton->hide();
285
286 float duration = m_mediaElement->duration();
287 if (isfinite(duration) || page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
288 m_timeline->setDuration(duration);
289 m_timelineContainer->show();
290 m_timeline->setPosition(m_mediaElement->currentTime());
291 updateTimeDisplay();
292 } else
293 m_timelineContainer->hide();
294
295 if (m_mediaElement->hasAudio() || page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
296 m_panelMuteButton->show();
297 else
298 m_panelMuteButton->hide();
299
300 if (m_volumeSlider)
301 m_volumeSlider->setVolume(m_mediaElement->volume());
302
303 if (m_toggleClosedCaptionsButton) {
304 if (m_mediaElement->hasClosedCaptions())
305 m_toggleClosedCaptionsButton->show();
306 else
307 m_toggleClosedCaptionsButton->hide();
308 }
309
310 if (m_mediaElement->movieLoadType() != MediaPlayer::LiveStream) {
311 m_returnToRealTimeButton->hide();
312 m_rewindButton->show();
313 } else {
314 m_returnToRealTimeButton->show();
315 m_rewindButton->hide();
316 }
317
318 makeOpaque();
319 }
320
playbackStarted()321 void MediaControlRootElement::playbackStarted()
322 {
323 m_playButton->updateDisplayType();
324 m_timeline->setPosition(m_mediaElement->currentTime());
325 updateTimeDisplay();
326 }
327
playbackProgressed()328 void MediaControlRootElement::playbackProgressed()
329 {
330 m_timeline->setPosition(m_mediaElement->currentTime());
331 updateTimeDisplay();
332 }
333
playbackStopped()334 void MediaControlRootElement::playbackStopped()
335 {
336 m_playButton->updateDisplayType();
337 m_timeline->setPosition(m_mediaElement->currentTime());
338 updateTimeDisplay();
339 makeOpaque();
340 }
341
updateTimeDisplay()342 void MediaControlRootElement::updateTimeDisplay()
343 {
344 float now = m_mediaElement->currentTime();
345 float duration = m_mediaElement->duration();
346
347 Page* page = document()->page();
348 if (!page)
349 return;
350
351 // Allow the theme to format the time.
352 ExceptionCode ec;
353 m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), ec);
354 m_currentTimeDisplay->setCurrentValue(now);
355 m_timeRemainingDisplay->setInnerText(page->theme()->formatMediaControlsRemainingTime(now, duration), ec);
356 m_timeRemainingDisplay->setCurrentValue(now - duration);
357 }
358
reportedError()359 void MediaControlRootElement::reportedError()
360 {
361 Page* page = document()->page();
362 if (!page)
363 return;
364
365 if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart))
366 m_timelineContainer->hide();
367
368 if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
369 m_panelMuteButton->hide();
370
371 m_fullScreenButton->hide();
372
373 if (m_volumeSliderContainer)
374 m_volumeSliderContainer->hide();
375 if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
376 m_toggleClosedCaptionsButton->hide();
377 }
378
changedNetworkState()379 void MediaControlRootElement::changedNetworkState()
380 {
381 if (m_statusDisplay)
382 m_statusDisplay->update();
383 }
384
loadedMetadata()385 void MediaControlRootElement::loadedMetadata()
386 {
387 if (m_statusDisplay)
388 m_statusDisplay->hide();
389
390 reset();
391 }
392
changedClosedCaptionsVisibility()393 void MediaControlRootElement::changedClosedCaptionsVisibility()
394 {
395 if (m_toggleClosedCaptionsButton)
396 m_toggleClosedCaptionsButton->updateDisplayType();
397 }
398
changedMute()399 void MediaControlRootElement::changedMute()
400 {
401 m_panelMuteButton->changedMute();
402 if (m_volumeSliderMuteButton)
403 m_volumeSliderMuteButton->changedMute();
404 }
405
changedVolume()406 void MediaControlRootElement::changedVolume()
407 {
408 if (m_volumeSlider)
409 m_volumeSlider->setVolume(m_mediaElement->volume());
410 }
411
enteredFullscreen()412 void MediaControlRootElement::enteredFullscreen()
413 {
414 if (m_mediaElement->movieLoadType() == MediaPlayer::LiveStream || m_mediaElement->movieLoadType() == MediaPlayer::StoredStream) {
415 #if !PLATFORM(ANDROID)
416 m_seekBackButton->hide();
417 m_seekForwardButton->hide();
418 #endif
419 } else
420 m_rewindButton->hide();
421 }
422
exitedFullscreen()423 void MediaControlRootElement::exitedFullscreen()
424 {
425 // "show" actually means removal of display:none style, so we are just clearing styles
426 // when exiting fullscreen.
427 // FIXME: Clarify naming of show/hide <http://webkit.org/b/58157>
428 m_rewindButton->show();
429 #if !PLATFORM(ANDROID)
430 m_seekBackButton->show();
431 m_seekForwardButton->show();
432 #endif
433 }
434
showVolumeSlider()435 void MediaControlRootElement::showVolumeSlider()
436 {
437 if (!m_mediaElement->hasAudio())
438 return;
439
440 if (m_volumeSliderContainer)
441 m_volumeSliderContainer->show();
442 }
443
shadowPseudoId() const444 const AtomicString& MediaControlRootElement::shadowPseudoId() const
445 {
446 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
447 return id;
448 }
449
450 }
451
452 #endif
453