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 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 67 PassRefPtr<MediaControls> MediaControls::create(HTMLMediaElement* mediaElement) 68 { 69 return MediaControlRootElement::create(mediaElement); 70 } 71 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 217 void MediaControlRootElement::show() 218 { 219 m_panel->show(); 220 } 221 222 void MediaControlRootElement::hide() 223 { 224 m_panel->hide(); 225 } 226 227 static const String& webkitTransitionString() 228 { 229 DEFINE_STATIC_LOCAL(String, s, ("-webkit-transition")); 230 return s; 231 } 232 233 static const String& opacityString() 234 { 235 DEFINE_STATIC_LOCAL(String, s, ("opacity")); 236 return s; 237 } 238 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 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 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 321 void MediaControlRootElement::playbackStarted() 322 { 323 m_playButton->updateDisplayType(); 324 m_timeline->setPosition(m_mediaElement->currentTime()); 325 updateTimeDisplay(); 326 } 327 328 void MediaControlRootElement::playbackProgressed() 329 { 330 m_timeline->setPosition(m_mediaElement->currentTime()); 331 updateTimeDisplay(); 332 } 333 334 void MediaControlRootElement::playbackStopped() 335 { 336 m_playButton->updateDisplayType(); 337 m_timeline->setPosition(m_mediaElement->currentTime()); 338 updateTimeDisplay(); 339 makeOpaque(); 340 } 341 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 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 379 void MediaControlRootElement::changedNetworkState() 380 { 381 if (m_statusDisplay) 382 m_statusDisplay->update(); 383 } 384 385 void MediaControlRootElement::loadedMetadata() 386 { 387 if (m_statusDisplay) 388 m_statusDisplay->hide(); 389 390 reset(); 391 } 392 393 void MediaControlRootElement::changedClosedCaptionsVisibility() 394 { 395 if (m_toggleClosedCaptionsButton) 396 m_toggleClosedCaptionsButton->updateDisplayType(); 397 } 398 399 void MediaControlRootElement::changedMute() 400 { 401 m_panelMuteButton->changedMute(); 402 if (m_volumeSliderMuteButton) 403 m_volumeSliderMuteButton->changedMute(); 404 } 405 406 void MediaControlRootElement::changedVolume() 407 { 408 if (m_volumeSlider) 409 m_volumeSlider->setVolume(m_mediaElement->volume()); 410 } 411 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 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 435 void MediaControlRootElement::showVolumeSlider() 436 { 437 if (!m_mediaElement->hasAudio()) 438 return; 439 440 if (m_volumeSliderContainer) 441 m_volumeSliderContainer->show(); 442 } 443 444 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