Home | History | Annotate | Download | only in shadow
      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