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 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 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 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 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 140 void MediaControls::loadedMetadata() 141 { 142 reset(); 143 } 144 145 void MediaControls::show() 146 { 147 makeOpaque(); 148 m_panel->setIsDisplayed(true); 149 m_panel->show(); 150 } 151 152 void MediaControls::hide() 153 { 154 m_panel->setIsDisplayed(false); 155 m_panel->hide(); 156 } 157 158 void MediaControls::makeOpaque() 159 { 160 m_panel->makeOpaque(); 161 } 162 163 void MediaControls::makeTransparent() 164 { 165 m_panel->makeTransparent(); 166 } 167 168 bool MediaControls::shouldHideControls() 169 { 170 return !m_panel->hovered(); 171 } 172 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 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 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 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 210 void MediaControls::showVolumeSlider() 211 { 212 if (!m_mediaController->hasAudio()) 213 return; 214 215 m_volumeSlider->show(); 216 } 217 218 void MediaControls::changedMute() 219 { 220 m_panelMuteButton->changedMute(); 221 } 222 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 231 void MediaControls::changedClosedCaptionsVisibility() 232 { 233 if (m_toggleClosedCaptionsButton) 234 m_toggleClosedCaptionsButton->updateDisplayType(); 235 } 236 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 248 void MediaControls::closedCaptionTracksChanged() 249 { 250 refreshClosedCaptionsButtonVisibility(); 251 } 252 253 void MediaControls::enteredFullscreen() 254 { 255 m_isFullscreen = true; 256 m_fullScreenButton->setIsFullscreen(true); 257 startHideFullscreenControlsTimer(); 258 } 259 260 void MediaControls::exitedFullscreen() 261 { 262 m_isFullscreen = false; 263 m_fullScreenButton->setIsFullscreen(false); 264 stopHideFullscreenControlsTimer(); 265 } 266 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 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 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 329 void MediaControls::stopHideFullscreenControlsTimer() 330 { 331 m_hideFullscreenControlsTimer.stop(); 332 } 333 334 const AtomicString& MediaControls::pseudo() const 335 { 336 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls")); 337 return id; 338 } 339 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 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 365 void MediaControls::showTextTrackDisplay() 366 { 367 if (!m_textDisplayContainer) 368 createTextTrackDisplay(); 369 m_textDisplayContainer->show(); 370 } 371 372 void MediaControls::hideTextTrackDisplay() 373 { 374 if (!m_textDisplayContainer) 375 createTextTrackDisplay(); 376 m_textDisplayContainer->hide(); 377 } 378 379 void MediaControls::updateTextTrackDisplay() 380 { 381 if (!m_textDisplayContainer) 382 createTextTrackDisplay(); 383 384 m_textDisplayContainer->updateDisplay(); 385 } 386 387 } 388