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