1 /* 2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 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 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "config.h" 31 #include "core/html/shadow/MediaControlElements.h" 32 33 #include "bindings/core/v8/ExceptionStatePlaceholder.h" 34 #include "core/dom/DOMTokenList.h" 35 #include "core/dom/shadow/ShadowRoot.h" 36 #include "core/events/MouseEvent.h" 37 #include "core/frame/LocalFrame.h" 38 #include "core/html/HTMLVideoElement.h" 39 #include "core/html/MediaController.h" 40 #include "core/html/shadow/MediaControls.h" 41 #include "core/html/track/TextTrack.h" 42 #include "core/html/track/vtt/VTTRegionList.h" 43 #include "core/page/EventHandler.h" 44 #include "core/rendering/RenderMediaControlElements.h" 45 #include "core/rendering/RenderSlider.h" 46 #include "core/rendering/RenderTheme.h" 47 #include "core/rendering/RenderVideo.h" 48 #include "platform/RuntimeEnabledFeatures.h" 49 50 namespace blink { 51 52 using namespace HTMLNames; 53 54 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId(); 55 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId(); 56 57 // This is the duration from mediaControls.css 58 static const double fadeOutDuration = 0.3; 59 60 static bool isUserInteractionEvent(Event* event) 61 { 62 const AtomicString& type = event->type(); 63 return type == EventTypeNames::mousedown 64 || type == EventTypeNames::mouseup 65 || type == EventTypeNames::click 66 || type == EventTypeNames::dblclick 67 || event->isKeyboardEvent() 68 || event->isTouchEvent(); 69 } 70 71 // Sliders (the volume control and timeline) need to capture some additional events used when dragging the thumb. 72 static bool isUserInteractionEventForSlider(Event* event) 73 { 74 const AtomicString& type = event->type(); 75 return type == EventTypeNames::mousedown 76 || type == EventTypeNames::mouseup 77 || type == EventTypeNames::click 78 || type == EventTypeNames::dblclick 79 || type == EventTypeNames::mouseover 80 || type == EventTypeNames::mouseout 81 || type == EventTypeNames::mousemove 82 || event->isKeyboardEvent() 83 || event->isTouchEvent(); 84 } 85 86 87 MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls) 88 : MediaControlDivElement(mediaControls, MediaControlsPanel) 89 , m_isDisplayed(false) 90 , m_opaque(true) 91 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired) 92 { 93 } 94 95 PassRefPtrWillBeRawPtr<MediaControlPanelElement> MediaControlPanelElement::create(MediaControls& mediaControls) 96 { 97 return adoptRefWillBeNoop(new MediaControlPanelElement(mediaControls)); 98 } 99 100 const AtomicString& MediaControlPanelElement::shadowPseudoId() const 101 { 102 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral)); 103 return id; 104 } 105 106 void MediaControlPanelElement::defaultEventHandler(Event* event) 107 { 108 // Suppress the media element activation behavior (toggle play/pause) when 109 // any part of the control panel is clicked. 110 if (event->type() == EventTypeNames::click) { 111 event->setDefaultHandled(); 112 return; 113 } 114 HTMLDivElement::defaultEventHandler(event); 115 } 116 117 void MediaControlPanelElement::startTimer() 118 { 119 stopTimer(); 120 121 // The timer is required to set the property display:'none' on the panel, 122 // such that captions are correctly displayed at the bottom of the video 123 // at the end of the fadeout transition. 124 // FIXME: Racing a transition with a setTimeout like this is wrong. 125 m_transitionTimer.startOneShot(fadeOutDuration, FROM_HERE); 126 } 127 128 void MediaControlPanelElement::stopTimer() 129 { 130 if (m_transitionTimer.isActive()) 131 m_transitionTimer.stop(); 132 } 133 134 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*) 135 { 136 if (!m_opaque) 137 hide(); 138 139 stopTimer(); 140 } 141 142 void MediaControlPanelElement::makeOpaque() 143 { 144 if (m_opaque) 145 return; 146 147 setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER); 148 m_opaque = true; 149 150 if (m_isDisplayed) 151 show(); 152 } 153 154 void MediaControlPanelElement::makeTransparent() 155 { 156 if (!m_opaque) 157 return; 158 159 setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER); 160 161 m_opaque = false; 162 startTimer(); 163 } 164 165 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed) 166 { 167 m_isDisplayed = isDisplayed; 168 } 169 170 bool MediaControlPanelElement::keepEventInNode(Event* event) 171 { 172 return isUserInteractionEvent(event); 173 } 174 175 // ---------------------------- 176 177 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(MediaControls& mediaControls) 178 // Mapping onto same MediaControlElementType as panel element, since it has similar properties. 179 : MediaControlDivElement(mediaControls, MediaControlsPanel) 180 { 181 } 182 183 PassRefPtrWillBeRawPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(MediaControls& mediaControls) 184 { 185 return adoptRefWillBeNoop(new MediaControlPanelEnclosureElement(mediaControls)); 186 } 187 188 const AtomicString& MediaControlPanelEnclosureElement::shadowPseudoId() const 189 { 190 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral)); 191 return id; 192 } 193 194 // ---------------------------- 195 196 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(MediaControls& mediaControls) 197 // Mapping onto same MediaControlElementType as panel element, since it has similar properties. 198 : MediaControlDivElement(mediaControls, MediaControlsPanel) 199 { 200 } 201 202 PassRefPtrWillBeRawPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(MediaControls& mediaControls) 203 { 204 return adoptRefWillBeNoop(new MediaControlOverlayEnclosureElement(mediaControls)); 205 } 206 207 const AtomicString& MediaControlOverlayEnclosureElement::shadowPseudoId() const 208 { 209 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral)); 210 return id; 211 } 212 213 void* MediaControlOverlayEnclosureElement::preDispatchEventHandler(Event* event) 214 { 215 // When the media element is clicked or touched we want to make the overlay cast button visible 216 // (if the other requirements are right) even if JavaScript is doing its own handling of the event. 217 // Doing it in preDispatchEventHandler prevents any interference from JavaScript. 218 // Note that we can't simply test for click, since JS handling of touch events can prevent their translation to click events. 219 if (event && (event->type() == EventTypeNames::click || event->type() == EventTypeNames::touchstart) && mediaElement().hasRemoteRoutes() && !mediaElement().shouldShowControls()) 220 mediaControls().showOverlayCastButton(); 221 return MediaControlDivElement::preDispatchEventHandler(event); 222 } 223 224 225 // ---------------------------- 226 227 MediaControlMuteButtonElement::MediaControlMuteButtonElement(MediaControls& mediaControls) 228 : MediaControlInputElement(mediaControls, MediaMuteButton) 229 { 230 } 231 232 PassRefPtrWillBeRawPtr<MediaControlMuteButtonElement> MediaControlMuteButtonElement::create(MediaControls& mediaControls) 233 { 234 RefPtrWillBeRawPtr<MediaControlMuteButtonElement> button = adoptRefWillBeNoop(new MediaControlMuteButtonElement(mediaControls)); 235 button->ensureUserAgentShadowRoot(); 236 button->setType("button"); 237 return button.release(); 238 } 239 240 void MediaControlMuteButtonElement::defaultEventHandler(Event* event) 241 { 242 if (event->type() == EventTypeNames::click) { 243 mediaElement().setMuted(!mediaElement().muted()); 244 event->setDefaultHandled(); 245 } 246 247 HTMLInputElement::defaultEventHandler(event); 248 } 249 250 void MediaControlMuteButtonElement::updateDisplayType() 251 { 252 setDisplayType(mediaElement().muted() ? MediaUnMuteButton : MediaMuteButton); 253 } 254 255 const AtomicString& MediaControlMuteButtonElement::shadowPseudoId() const 256 { 257 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral)); 258 return id; 259 } 260 261 // ---------------------------- 262 263 MediaControlPlayButtonElement::MediaControlPlayButtonElement(MediaControls& mediaControls) 264 : MediaControlInputElement(mediaControls, MediaPlayButton) 265 { 266 } 267 268 PassRefPtrWillBeRawPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(MediaControls& mediaControls) 269 { 270 RefPtrWillBeRawPtr<MediaControlPlayButtonElement> button = adoptRefWillBeNoop(new MediaControlPlayButtonElement(mediaControls)); 271 button->ensureUserAgentShadowRoot(); 272 button->setType("button"); 273 return button.release(); 274 } 275 276 void MediaControlPlayButtonElement::defaultEventHandler(Event* event) 277 { 278 if (event->type() == EventTypeNames::click) { 279 mediaElement().togglePlayState(); 280 updateDisplayType(); 281 event->setDefaultHandled(); 282 } 283 HTMLInputElement::defaultEventHandler(event); 284 } 285 286 void MediaControlPlayButtonElement::updateDisplayType() 287 { 288 setDisplayType(mediaElement().togglePlayStateWillPlay() ? MediaPlayButton : MediaPauseButton); 289 } 290 291 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const 292 { 293 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral)); 294 return id; 295 } 296 297 // ---------------------------- 298 299 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(MediaControls& mediaControls) 300 : MediaControlInputElement(mediaControls, MediaOverlayPlayButton) 301 { 302 } 303 304 PassRefPtrWillBeRawPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(MediaControls& mediaControls) 305 { 306 RefPtrWillBeRawPtr<MediaControlOverlayPlayButtonElement> button = adoptRefWillBeNoop(new MediaControlOverlayPlayButtonElement(mediaControls)); 307 button->ensureUserAgentShadowRoot(); 308 button->setType("button"); 309 return button.release(); 310 } 311 312 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event) 313 { 314 if (event->type() == EventTypeNames::click && mediaElement().togglePlayStateWillPlay()) { 315 mediaElement().togglePlayState(); 316 updateDisplayType(); 317 event->setDefaultHandled(); 318 } 319 } 320 321 void MediaControlOverlayPlayButtonElement::updateDisplayType() 322 { 323 if (mediaElement().shouldShowControls() && mediaElement().togglePlayStateWillPlay()) 324 show(); 325 else 326 hide(); 327 } 328 329 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const 330 { 331 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral)); 332 return id; 333 } 334 335 bool MediaControlOverlayPlayButtonElement::keepEventInNode(Event* event) 336 { 337 return isUserInteractionEvent(event); 338 } 339 340 341 // ---------------------------- 342 343 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(MediaControls& mediaControls) 344 : MediaControlInputElement(mediaControls, MediaShowClosedCaptionsButton) 345 { 346 } 347 348 PassRefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(MediaControls& mediaControls) 349 { 350 RefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRefWillBeNoop(new MediaControlToggleClosedCaptionsButtonElement(mediaControls)); 351 button->ensureUserAgentShadowRoot(); 352 button->setType("button"); 353 button->hide(); 354 return button.release(); 355 } 356 357 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() 358 { 359 bool captionsVisible = mediaElement().closedCaptionsVisible(); 360 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); 361 setChecked(captionsVisible); 362 } 363 364 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event) 365 { 366 if (event->type() == EventTypeNames::click) { 367 mediaElement().setClosedCaptionsVisible(!mediaElement().closedCaptionsVisible()); 368 setChecked(mediaElement().closedCaptionsVisible()); 369 updateDisplayType(); 370 event->setDefaultHandled(); 371 } 372 373 HTMLInputElement::defaultEventHandler(event); 374 } 375 376 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const 377 { 378 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral)); 379 return id; 380 } 381 382 // ---------------------------- 383 384 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaControls) 385 : MediaControlInputElement(mediaControls, MediaSlider) 386 { 387 } 388 389 PassRefPtrWillBeRawPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(MediaControls& mediaControls) 390 { 391 RefPtrWillBeRawPtr<MediaControlTimelineElement> timeline = adoptRefWillBeNoop(new MediaControlTimelineElement(mediaControls)); 392 timeline->ensureUserAgentShadowRoot(); 393 timeline->setType("range"); 394 timeline->setAttribute(stepAttr, "any"); 395 return timeline.release(); 396 } 397 398 void MediaControlTimelineElement::defaultEventHandler(Event* event) 399 { 400 if (event->isMouseEvent() && toMouseEvent(event)->button() != LeftButton) 401 return; 402 403 if (!inDocument() || !document().isActive()) 404 return; 405 406 if (event->type() == EventTypeNames::mousedown) 407 mediaControls().beginScrubbing(); 408 409 if (event->type() == EventTypeNames::mouseup) 410 mediaControls().endScrubbing(); 411 412 MediaControlInputElement::defaultEventHandler(event); 413 414 if (event->type() == EventTypeNames::mouseover || event->type() == EventTypeNames::mouseout || event->type() == EventTypeNames::mousemove) 415 return; 416 417 double time = value().toDouble(); 418 if (event->type() == EventTypeNames::input) { 419 // FIXME: This will need to take the timeline offset into consideration 420 // once that concept is supported, see https://crbug.com/312699 421 if (mediaElement().controller()) 422 mediaElement().controller()->setCurrentTime(time); 423 else 424 mediaElement().setCurrentTime(time, IGNORE_EXCEPTION); 425 } 426 427 RenderSlider* slider = toRenderSlider(renderer()); 428 if (slider && slider->inDragMode()) 429 mediaControls().updateCurrentTimeDisplay(); 430 } 431 432 bool MediaControlTimelineElement::willRespondToMouseClickEvents() 433 { 434 return inDocument() && document().isActive(); 435 } 436 437 void MediaControlTimelineElement::setPosition(double currentTime) 438 { 439 setValue(String::number(currentTime)); 440 } 441 442 void MediaControlTimelineElement::setDuration(double duration) 443 { 444 setFloatingPointAttribute(maxAttr, std::isfinite(duration) ? duration : 0); 445 } 446 447 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const 448 { 449 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral)); 450 return id; 451 } 452 453 bool MediaControlTimelineElement::keepEventInNode(Event* event) 454 { 455 return isUserInteractionEventForSlider(event); 456 } 457 458 // ---------------------------- 459 460 MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(MediaControls& mediaControls) 461 : MediaControlInputElement(mediaControls, MediaVolumeSlider) 462 { 463 } 464 465 PassRefPtrWillBeRawPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(MediaControls& mediaControls) 466 { 467 RefPtrWillBeRawPtr<MediaControlVolumeSliderElement> slider = adoptRefWillBeNoop(new MediaControlVolumeSliderElement(mediaControls)); 468 slider->ensureUserAgentShadowRoot(); 469 slider->setType("range"); 470 slider->setAttribute(stepAttr, "any"); 471 slider->setAttribute(maxAttr, "1"); 472 return slider.release(); 473 } 474 475 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event) 476 { 477 if (event->isMouseEvent() && toMouseEvent(event)->button() != LeftButton) 478 return; 479 480 if (!inDocument() || !document().isActive()) 481 return; 482 483 MediaControlInputElement::defaultEventHandler(event); 484 485 if (event->type() == EventTypeNames::mouseover || event->type() == EventTypeNames::mouseout || event->type() == EventTypeNames::mousemove) 486 return; 487 488 double volume = value().toDouble(); 489 mediaElement().setVolume(volume, ASSERT_NO_EXCEPTION); 490 mediaElement().setMuted(false); 491 } 492 493 bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents() 494 { 495 if (!inDocument() || !document().isActive()) 496 return false; 497 498 return MediaControlInputElement::willRespondToMouseMoveEvents(); 499 } 500 501 bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents() 502 { 503 if (!inDocument() || !document().isActive()) 504 return false; 505 506 return MediaControlInputElement::willRespondToMouseClickEvents(); 507 } 508 509 void MediaControlVolumeSliderElement::setVolume(double volume) 510 { 511 if (value().toDouble() != volume) 512 setValue(String::number(volume)); 513 } 514 515 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const 516 { 517 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral)); 518 return id; 519 } 520 521 bool MediaControlVolumeSliderElement::keepEventInNode(Event* event) 522 { 523 return isUserInteractionEventForSlider(event); 524 } 525 526 // ---------------------------- 527 528 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(MediaControls& mediaControls) 529 : MediaControlInputElement(mediaControls, MediaEnterFullscreenButton) 530 { 531 } 532 533 PassRefPtrWillBeRawPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(MediaControls& mediaControls) 534 { 535 RefPtrWillBeRawPtr<MediaControlFullscreenButtonElement> button = adoptRefWillBeNoop(new MediaControlFullscreenButtonElement(mediaControls)); 536 button->ensureUserAgentShadowRoot(); 537 button->setType("button"); 538 button->hide(); 539 return button.release(); 540 } 541 542 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) 543 { 544 if (event->type() == EventTypeNames::click) { 545 if (mediaElement().isFullscreen()) 546 mediaElement().exitFullscreen(); 547 else 548 mediaElement().enterFullscreen(); 549 event->setDefaultHandled(); 550 } 551 HTMLInputElement::defaultEventHandler(event); 552 } 553 554 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const 555 { 556 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral)); 557 return id; 558 } 559 560 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen) 561 { 562 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton); 563 } 564 565 // ---------------------------- 566 567 MediaControlCastButtonElement::MediaControlCastButtonElement(MediaControls& mediaControls, bool isOverlayButton) 568 : MediaControlInputElement(mediaControls, MediaCastOnButton), m_isOverlayButton(isOverlayButton) 569 { 570 } 571 572 PassRefPtrWillBeRawPtr<MediaControlCastButtonElement> MediaControlCastButtonElement::create(MediaControls& mediaControls, bool isOverlayButton) 573 { 574 RefPtrWillBeRawPtr<MediaControlCastButtonElement> button = adoptRefWillBeNoop(new MediaControlCastButtonElement(mediaControls, isOverlayButton)); 575 button->ensureUserAgentShadowRoot(); 576 button->setType("button"); 577 return button.release(); 578 } 579 580 void MediaControlCastButtonElement::defaultEventHandler(Event* event) 581 { 582 if (event->type() == EventTypeNames::click) { 583 if (mediaElement().isPlayingRemotely()) { 584 mediaElement().requestRemotePlaybackControl(); 585 } else { 586 mediaElement().requestRemotePlayback(); 587 } 588 } 589 HTMLInputElement::defaultEventHandler(event); 590 } 591 592 const AtomicString& MediaControlCastButtonElement::shadowPseudoId() const 593 { 594 DEFINE_STATIC_LOCAL(AtomicString, id_nonOverlay, ("-internal-media-controls-cast-button", AtomicString::ConstructFromLiteral)); 595 DEFINE_STATIC_LOCAL(AtomicString, id_overlay, ("-internal-media-controls-overlay-cast-button", AtomicString::ConstructFromLiteral)); 596 return m_isOverlayButton ? id_overlay : id_nonOverlay; 597 } 598 599 void MediaControlCastButtonElement::setIsPlayingRemotely(bool isPlayingRemotely) 600 { 601 setDisplayType(isPlayingRemotely ? MediaCastOnButton : MediaCastOffButton); 602 } 603 604 bool MediaControlCastButtonElement::keepEventInNode(Event* event) 605 { 606 return isUserInteractionEvent(event); 607 } 608 609 // ---------------------------- 610 611 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(MediaControls& mediaControls) 612 : MediaControlTimeDisplayElement(mediaControls, MediaTimeRemainingDisplay) 613 { 614 } 615 616 PassRefPtrWillBeRawPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(MediaControls& mediaControls) 617 { 618 return adoptRefWillBeNoop(new MediaControlTimeRemainingDisplayElement(mediaControls)); 619 } 620 621 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId() 622 { 623 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral)); 624 return id; 625 } 626 627 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const 628 { 629 return getMediaControlTimeRemainingDisplayElementShadowPseudoId(); 630 } 631 632 // ---------------------------- 633 634 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(MediaControls& mediaControls) 635 : MediaControlTimeDisplayElement(mediaControls, MediaCurrentTimeDisplay) 636 { 637 } 638 639 PassRefPtrWillBeRawPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(MediaControls& mediaControls) 640 { 641 return adoptRefWillBeNoop(new MediaControlCurrentTimeDisplayElement(mediaControls)); 642 } 643 644 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId() 645 { 646 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral)); 647 return id; 648 } 649 650 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const 651 { 652 return getMediaControlCurrentTimeDisplayElementShadowPseudoId(); 653 } 654 655 // ---------------------------- 656 657 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(MediaControls& mediaControls) 658 : MediaControlDivElement(mediaControls, MediaTextTrackDisplayContainer) 659 , m_fontSize(0) 660 { 661 } 662 663 PassRefPtrWillBeRawPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(MediaControls& mediaControls) 664 { 665 RefPtrWillBeRawPtr<MediaControlTextTrackContainerElement> element = adoptRefWillBeNoop(new MediaControlTextTrackContainerElement(mediaControls)); 666 element->hide(); 667 return element.release(); 668 } 669 670 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderStyle*) 671 { 672 return new RenderTextTrackContainerElement(this); 673 } 674 675 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId() 676 { 677 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral)); 678 return id; 679 } 680 681 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const 682 { 683 return textTrackContainerElementShadowPseudoId(); 684 } 685 686 void MediaControlTextTrackContainerElement::updateDisplay() 687 { 688 if (!mediaElement().closedCaptionsVisible()) { 689 removeChildren(); 690 return; 691 } 692 693 // 1. If the media element is an audio element, or is another playback 694 // mechanism with no rendering area, abort these steps. There is nothing to 695 // render. 696 if (isHTMLAudioElement(mediaElement())) 697 return; 698 699 // 2. Let video be the media element or other playback mechanism. 700 HTMLVideoElement& video = toHTMLVideoElement(mediaElement()); 701 702 // 3. Let output be an empty list of absolutely positioned CSS block boxes. 703 704 // 4. If the user agent is exposing a user interface for video, add to 705 // output one or more completely transparent positioned CSS block boxes that 706 // cover the same region as the user interface. 707 708 // 5. If the last time these rules were run, the user agent was not exposing 709 // a user interface for video, but now it is, let reset be true. Otherwise, 710 // let reset be false. 711 712 // There is nothing to be done explicitly for 4th and 5th steps, as 713 // everything is handled through CSS. The caption box is on top of the 714 // controls box, in a container set with the -webkit-box display property. 715 716 // 6. Let tracks be the subset of video's list of text tracks that have as 717 // their rules for updating the text track rendering these rules for 718 // updating the display of WebVTT text tracks, and whose text track mode is 719 // showing or showing by default. 720 // 7. Let cues be an empty list of text track cues. 721 // 8. For each track track in tracks, append to cues all the cues from 722 // track's list of cues that have their text track cue active flag set. 723 CueList activeCues = video.currentlyActiveCues(); 724 725 // 9. If reset is false, then, for each text track cue cue in cues: if cue's 726 // text track cue display state has a set of CSS boxes, then add those boxes 727 // to output, and remove cue from cues. 728 729 // There is nothing explicitly to be done here, as all the caching occurs 730 // within the TextTrackCue instance itself. If parameters of the cue change, 731 // the display tree is cleared. 732 733 // 10. For each text track cue cue in cues that has not yet had 734 // corresponding CSS boxes added to output, in text track cue order, run the 735 // following substeps: 736 for (size_t i = 0; i < activeCues.size(); ++i) { 737 TextTrackCue* cue = activeCues[i].data(); 738 739 ASSERT(cue->isActive()); 740 if (!cue->track() || !cue->track()->isRendered() || !cue->isActive()) 741 continue; 742 743 cue->updateDisplay(m_videoDisplaySize.size(), *this); 744 } 745 746 // 11. Return output. 747 if (hasChildren()) 748 show(); 749 else 750 hide(); 751 } 752 753 void MediaControlTextTrackContainerElement::updateSizes() 754 { 755 if (!document().isActive()) 756 return; 757 758 IntRect videoBox; 759 760 if (!mediaElement().renderer() || !mediaElement().renderer()->isVideo()) 761 return; 762 videoBox = toRenderVideo(mediaElement().renderer())->videoBox(); 763 764 if (m_videoDisplaySize == videoBox) 765 return; 766 m_videoDisplaySize = videoBox; 767 768 float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width()); 769 770 float fontSize = smallestDimension * 0.05f; 771 if (fontSize != m_fontSize) { 772 m_fontSize = fontSize; 773 setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX); 774 } 775 } 776 777 // ---------------------------- 778 779 } // namespace blink 780