2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "MediaControlElements.h"
35 #include "CSSStyleDeclaration.h"
36 #include "CSSValueKeywords.h"
37 #include "DOMTokenList.h"
38 #include "EventNames.h"
39 #include "FloatConversion.h"
40 #include "FloatPoint.h"
42 #include "HTMLDivElement.h"
43 #include "HTMLMediaElement.h"
44 #include "HTMLNames.h"
45 #include "HTMLVideoElement.h"
46 #include "LayoutRepainter.h"
47 #include "LocalizedStrings.h"
48 #include "MediaControls.h"
49 #include "MouseEvent.h"
51 #include "RenderDeprecatedFlexibleBox.h"
52 #include "RenderInline.h"
53 #include "RenderMedia.h"
54 #include "RenderSlider.h"
55 #include "RenderText.h"
56 #include "RenderTheme.h"
57 #include "RenderVideo.h"
58 #include "RenderView.h"
59 #include "ScriptController.h"
61 #include "StyleResolver.h"
66 using namespace HTMLNames;
69 // FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
71 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
72 static const float cSeekForwardTime = 5.0f;
73 static const float cSeekBackTime = (-5.0f);
75 static const float cSkipRepeatDelay = 0.1f;
76 static const float cSkipTime = 0.2f;
78 static const float cScanRepeatDelay = 1.5f;
79 static const float cScanMaximumRate = 8;
81 HTMLMediaElement* toParentMediaElement(Node* node)
85 Node* mediaNode = node->shadowHost();
88 if (!mediaNode || !mediaNode->isElementNode() || !static_cast<Element*>(mediaNode)->isMediaElement())
91 return static_cast<HTMLMediaElement*>(mediaNode);
94 MediaControlElementType mediaControlElementType(Node* node)
96 ASSERT(node->isMediaControlElement());
97 HTMLElement* element = toHTMLElement(node);
98 if (element->hasTagName(inputTag))
99 return static_cast<MediaControlInputElement*>(element)->displayType();
100 return static_cast<MediaControlElement*>(element)->displayType();
103 // ----------------------------
105 MediaControlElement::MediaControlElement(Document* document)
106 : HTMLDivElement(divTag, document)
107 , m_mediaController(0)
111 void MediaControlElement::show()
113 removeInlineStyleProperty(CSSPropertyDisplay);
116 void MediaControlElement::hide()
118 setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
121 // ----------------------------
123 inline MediaControlPanelElement::MediaControlPanelElement(Document* document)
124 : MediaControlElement(document)
125 , m_canBeDragged(false)
126 , m_isBeingDragged(false)
127 , m_isDisplayed(false)
129 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
133 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document)
135 return adoptRef(new MediaControlPanelElement(document));
138 MediaControlElementType MediaControlPanelElement::displayType() const
140 return MediaControlsPanel;
143 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
145 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel"));
149 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
154 if (m_isBeingDragged)
157 RenderObject* renderer = this->renderer();
158 if (!renderer || !renderer->isBox())
161 Frame* frame = document()->frame();
165 m_dragStartEventLocation = eventLocation;
167 frame->eventHandler()->setCapturingMouseEventsNode(this);
169 m_isBeingDragged = true;
172 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
174 if (!m_isBeingDragged)
177 LayoutSize distanceDragged = eventLocation - m_dragStartEventLocation;
178 setPosition(LayoutPoint(distanceDragged.width(), distanceDragged.height()));
181 void MediaControlPanelElement::endDrag()
183 if (!m_isBeingDragged)
186 m_isBeingDragged = false;
188 Frame* frame = document()->frame();
192 frame->eventHandler()->setCapturingMouseEventsNode(0);
195 void MediaControlPanelElement::startTimer()
199 // The timer is required to set the property display:'none' on the panel,
200 // such that captions are correctly displayed at the bottom of the video
201 // at the end of the fadeout transition.
202 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
203 m_transitionTimer.startOneShot(duration);
206 void MediaControlPanelElement::stopTimer()
208 if (m_transitionTimer.isActive())
209 m_transitionTimer.stop();
213 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
221 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
223 double left = position.x();
224 double top = position.y();
226 // Set the left and top to control the panel's position; this depends on it being absolute positioned.
227 // Set the margin to zero since the position passed in will already include the effect of the margin.
228 setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
229 setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
230 setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
231 setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
233 ExceptionCode ignored;
234 classList()->add("dragged", ignored);
237 void MediaControlPanelElement::resetPosition()
239 removeInlineStyleProperty(CSSPropertyLeft);
240 removeInlineStyleProperty(CSSPropertyTop);
241 removeInlineStyleProperty(CSSPropertyMarginLeft);
242 removeInlineStyleProperty(CSSPropertyMarginTop);
244 ExceptionCode ignored;
245 classList()->remove("dragged", ignored);
248 void MediaControlPanelElement::makeOpaque()
253 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0;
255 setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
256 setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
257 setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
265 void MediaControlPanelElement::makeTransparent()
270 setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
271 setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, document()->page()->theme()->mediaControlsFadeOutDuration(), CSSPrimitiveValue::CSS_S);
272 setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
278 void MediaControlPanelElement::defaultEventHandler(Event* event)
280 MediaControlElement::defaultEventHandler(event);
282 if (event->isMouseEvent()) {
283 LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
284 if (event->type() == eventNames().mousedownEvent && event->target() == this) {
286 event->setDefaultHandled();
287 } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
288 continueDrag(location);
289 else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
290 continueDrag(location);
292 event->setDefaultHandled();
297 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
299 if (m_canBeDragged == canBeDragged)
302 m_canBeDragged = canBeDragged;
308 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
310 m_isDisplayed = isDisplayed;
313 // ----------------------------
315 inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document)
316 : MediaControlElement(document)
320 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document* document)
322 RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document));
324 return element.release();
327 MediaControlElementType MediaControlTimelineContainerElement::displayType() const
329 return MediaTimelineContainer;
332 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
334 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container"));
338 // ----------------------------
340 class RenderMediaVolumeSliderContainer : public RenderBlock {
342 RenderMediaVolumeSliderContainer(Node*);
345 virtual void layout();
348 RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node)
353 void RenderMediaVolumeSliderContainer::layout()
355 RenderBlock::layout();
357 if (style()->display() == NONE || !nextSibling() || !nextSibling()->isBox())
360 RenderBox* buttonBox = toRenderBox(nextSibling());
361 int absoluteOffsetTop = buttonBox->localToAbsolute(FloatPoint(0, -size().height())).y();
363 LayoutStateDisabler layoutStateDisabler(view());
365 // If the slider would be rendered outside the page, it should be moved below the controls.
366 if (UNLIKELY(absoluteOffsetTop < 0))
367 setY(buttonBox->offsetTop() + theme()->volumeSliderOffsetFromMuteButton(buttonBox, pixelSnappedSize()).y());
370 inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* document)
371 : MediaControlElement(document)
375 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document* document)
377 RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document));
379 return element.release();
382 RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
384 return new (arena) RenderMediaVolumeSliderContainer(this);
387 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
389 if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
392 // Poor man's mouseleave event detection.
393 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
394 if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
397 if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
403 MediaControlElementType MediaControlVolumeSliderContainerElement::displayType() const
405 return MediaVolumeSliderContainer;
408 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
410 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container"));
414 // ----------------------------
416 inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document)
417 : MediaControlElement(document)
418 , m_stateBeingDisplayed(Nothing)
422 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document* document)
424 RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(document));
426 return element.release();
429 void MediaControlStatusDisplayElement::update()
431 // Get the new state that we'll have to display.
432 StateBeingDisplayed newStateToDisplay = Nothing;
434 if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc())
435 newStateToDisplay = Loading;
436 else if (mediaController()->isLiveStream())
437 newStateToDisplay = LiveBroadcast;
439 if (newStateToDisplay == m_stateBeingDisplayed)
444 if (m_stateBeingDisplayed == Nothing)
446 else if (newStateToDisplay == Nothing)
449 m_stateBeingDisplayed = newStateToDisplay;
451 switch (m_stateBeingDisplayed) {
456 setInnerText(mediaElementLoadingStateText(), e);
459 setInnerText(mediaElementLiveBroadcastStateText(), e);
464 MediaControlElementType MediaControlStatusDisplayElement::displayType() const
466 return MediaStatusDisplay;
469 const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
471 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display"));
475 // ----------------------------
477 MediaControlInputElement::MediaControlInputElement(Document* document, MediaControlElementType displayType)
478 : HTMLInputElement(inputTag, document, 0, false)
479 , m_mediaController(0)
480 , m_displayType(displayType)
484 void MediaControlInputElement::show()
486 removeInlineStyleProperty(CSSPropertyDisplay);
489 void MediaControlInputElement::hide()
491 setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
495 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
497 if (displayType == m_displayType)
500 m_displayType = displayType;
501 if (RenderObject* object = renderer())
505 // ----------------------------
507 inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, MediaControlElementType displayType)
508 : MediaControlInputElement(document, displayType)
512 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
514 if (event->type() == eventNames().clickEvent) {
515 mediaController()->setMuted(!mediaController()->muted());
516 event->setDefaultHandled();
519 HTMLInputElement::defaultEventHandler(event);
522 void MediaControlMuteButtonElement::changedMute()
527 void MediaControlMuteButtonElement::updateDisplayType()
529 setDisplayType(mediaController()->muted() ? MediaUnMuteButton : MediaMuteButton);
532 // ----------------------------
534 inline MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls)
535 : MediaControlMuteButtonElement(document, MediaMuteButton)
536 , m_controls(controls)
540 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls)
544 RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
545 button->createShadowSubtree();
546 button->setType("button");
547 return button.release();
550 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
552 if (event->type() == eventNames().mouseoverEvent)
553 m_controls->showVolumeSlider();
555 MediaControlMuteButtonElement::defaultEventHandler(event);
558 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
560 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button"));
564 // ----------------------------
566 inline MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document)
567 : MediaControlMuteButtonElement(document, MediaMuteButton)
571 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document)
573 RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
574 button->createShadowSubtree();
575 button->setType("button");
576 return button.release();
579 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
581 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button"));
585 // ----------------------------
587 inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document)
588 : MediaControlInputElement(document, MediaPlayButton)
592 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document)
594 RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
595 button->createShadowSubtree();
596 button->setType("button");
597 return button.release();
600 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
602 if (event->type() == eventNames().clickEvent) {
603 if (mediaController()->canPlay())
604 mediaController()->play();
606 mediaController()->pause();
608 event->setDefaultHandled();
610 HTMLInputElement::defaultEventHandler(event);
613 void MediaControlPlayButtonElement::updateDisplayType()
615 setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
618 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
620 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button"));
624 // ----------------------------
626 inline MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document)
627 : MediaControlInputElement(document, MediaOverlayPlayButton)
631 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document)
633 RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
634 button->createShadowSubtree();
635 button->setType("button");
636 return button.release();
639 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
641 if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
642 mediaController()->play();
644 event->setDefaultHandled();
646 HTMLInputElement::defaultEventHandler(event);
649 void MediaControlOverlayPlayButtonElement::updateDisplayType()
651 if (mediaController()->canPlay()) {
653 setDisplayType(MediaOverlayPlayButton);
658 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
660 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button"));
664 // ----------------------------
666 inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, MediaControlElementType displayType)
667 : MediaControlInputElement(document, displayType)
668 , m_actionOnStop(Nothing)
670 , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
674 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
676 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
677 if (event->type() == eventNames().clickEvent) {
678 ExceptionCode ignoredCode;
679 float seekTime = isForwardButton() ? cSeekForwardTime : cSeekBackTime;
680 mediaController()->setCurrentTime(mediaController()->currentTime() + seekTime, ignoredCode);
681 event->setDefaultHandled();
684 // Set the mousedown and mouseup events as defaultHandled so they
685 // do not trigger drag start or end actions in MediaControlPanelElement.
686 if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
687 event->setDefaultHandled();
691 void MediaControlSeekButtonElement::setActive(bool flag, bool pause)
693 if (flag == active())
701 MediaControlInputElement::setActive(flag, pause);
704 void MediaControlSeekButtonElement::startTimer()
706 m_seekType = mediaController()->supportsScanning() ? Scan : Skip;
708 if (m_seekType == Skip) {
709 // Seeking by skipping requires the video to be paused during seeking.
710 m_actionOnStop = mediaController()->paused() ? Nothing : Play;
711 mediaController()->pause();
713 // Seeking by scanning requires the video to be playing during seeking.
714 m_actionOnStop = mediaController()->paused() ? Pause : Nothing;
715 mediaController()->play();
716 mediaController()->setPlaybackRate(nextRate());
719 m_seekTimer.start(0, m_seekType == Skip ? cSkipRepeatDelay : cScanRepeatDelay);
722 void MediaControlSeekButtonElement::stopTimer()
724 if (m_seekType == Scan)
725 mediaController()->setPlaybackRate(mediaController()->defaultPlaybackRate());
727 if (m_actionOnStop == Play)
728 mediaController()->play();
729 else if (m_actionOnStop == Pause)
730 mediaController()->pause();
732 if (m_seekTimer.isActive())
736 float MediaControlSeekButtonElement::nextRate() const
738 float rate = std::min(cScanMaximumRate, fabsf(mediaController()->playbackRate() * 2));
739 if (!isForwardButton())
744 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
746 if (m_seekType == Skip) {
748 float skipTime = isForwardButton() ? cSkipTime : -cSkipTime;
749 mediaController()->setCurrentTime(mediaController()->currentTime() + skipTime, ec);
751 mediaController()->setPlaybackRate(nextRate());
754 // ----------------------------
756 inline MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document* document)
757 : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
761 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document* document)
763 RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document));
764 button->createShadowSubtree();
765 button->setType("button");
766 return button.release();
769 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
771 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button"));
775 // ----------------------------
777 inline MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document* document)
778 : MediaControlSeekButtonElement(document, MediaSeekBackButton)
782 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document* document)
784 RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document));
785 button->createShadowSubtree();
786 button->setType("button");
787 return button.release();
790 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
792 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button"));
796 // ----------------------------
798 inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document)
799 : MediaControlInputElement(document, MediaRewindButton)
803 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document* document)
805 RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document));
806 button->createShadowSubtree();
807 button->setType("button");
808 return button.release();
811 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
813 if (event->type() == eventNames().clickEvent) {
814 ExceptionCode ignoredCode;
815 mediaController()->setCurrentTime(max(0.0f, mediaController()->currentTime() - 30), ignoredCode);
816 event->setDefaultHandled();
818 HTMLInputElement::defaultEventHandler(event);
821 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
823 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button"));
827 // ----------------------------
829 inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document)
830 : MediaControlInputElement(document, MediaReturnToRealtimeButton)
834 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document* document)
836 RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document));
837 button->createShadowSubtree();
838 button->setType("button");
840 return button.release();
843 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
845 if (event->type() == eventNames().clickEvent) {
846 mediaController()->returnToRealtime();
847 event->setDefaultHandled();
849 HTMLInputElement::defaultEventHandler(event);
852 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
854 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button"));
858 // ----------------------------
860 inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document)
861 : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
865 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document)
867 RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document));
868 button->createShadowSubtree();
869 button->setType("button");
871 return button.release();
874 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
876 if (event->type() == eventNames().clickEvent) {
877 mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
878 setChecked(mediaController()->closedCaptionsVisible());
880 event->setDefaultHandled();
883 HTMLInputElement::defaultEventHandler(event);
886 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
888 setDisplayType(mediaController()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
891 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
893 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button"));
897 // ----------------------------
899 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls)
900 : MediaControlInputElement(document, MediaSlider)
901 , m_controls(controls)
905 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls)
909 RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
910 timeline->createShadowSubtree();
911 timeline->setType("range");
912 timeline->setAttribute(precisionAttr, "float");
913 return timeline.release();
916 void MediaControlTimelineElement::defaultEventHandler(Event* event)
918 // Left button is 0. Rejects mouse events not from left button.
919 if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
925 if (event->type() == eventNames().mousedownEvent)
926 mediaController()->beginScrubbing();
928 if (event->type() == eventNames().mouseupEvent)
929 mediaController()->endScrubbing();
931 MediaControlInputElement::defaultEventHandler(event);
933 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
936 float time = narrowPrecisionToFloat(value().toDouble());
937 if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime()) {
939 mediaController()->setCurrentTime(time, ec);
942 RenderSlider* slider = toRenderSlider(renderer());
943 if (slider && slider->inDragMode())
944 m_controls->updateTimeDisplay();
947 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
955 void MediaControlTimelineElement::setPosition(float currentTime)
957 setValue(String::number(currentTime));
960 void MediaControlTimelineElement::setDuration(float duration)
962 setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
966 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
968 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline"));
972 // ----------------------------
974 inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document)
975 : MediaControlInputElement(document, MediaVolumeSlider)
976 , m_clearMutedOnUserInteraction(false)
980 PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(Document* document)
982 RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(document));
983 slider->createShadowSubtree();
984 slider->setType("range");
985 slider->setAttribute(precisionAttr, "float");
986 slider->setAttribute(maxAttr, "1");
987 return slider.release();
990 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
992 // Left button is 0. Rejects mouse events not from left button.
993 if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
999 MediaControlInputElement::defaultEventHandler(event);
1001 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
1004 float volume = narrowPrecisionToFloat(value().toDouble());
1005 if (volume != mediaController()->volume()) {
1006 ExceptionCode ec = 0;
1007 mediaController()->setVolume(volume, ec);
1010 if (m_clearMutedOnUserInteraction)
1011 mediaController()->setMuted(false);
1014 bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents()
1019 return MediaControlInputElement::willRespondToMouseMoveEvents();
1022 bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents()
1027 return MediaControlInputElement::willRespondToMouseClickEvents();
1030 void MediaControlVolumeSliderElement::setVolume(float volume)
1032 if (value().toFloat() != volume)
1033 setValue(String::number(volume));
1036 void MediaControlVolumeSliderElement::setClearMutedOnUserInteraction(bool clearMute)
1038 m_clearMutedOnUserInteraction = clearMute;
1041 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const
1043 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider"));
1047 // ----------------------------
1049 inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document* document)
1050 : MediaControlVolumeSliderElement(document)
1054 PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document* document)
1056 RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(document));
1057 slider->createShadowSubtree();
1058 slider->setType("range");
1059 slider->setAttribute(precisionAttr, "float");
1060 slider->setAttribute(maxAttr, "1");
1061 return slider.release();
1064 const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
1066 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider"));
1070 // ----------------------------
1072 inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, MediaControls*)
1073 : MediaControlInputElement(document, MediaEnterFullscreenButton)
1077 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document, MediaControls* controls)
1081 RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document, controls));
1082 button->createShadowSubtree();
1083 button->setType("button");
1085 return button.release();
1088 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
1090 if (event->type() == eventNames().clickEvent) {
1091 #if ENABLE(FULLSCREEN_API)
1092 // Only use the new full screen API if the fullScreenEnabled setting has
1093 // been explicitly enabled. Otherwise, use the old fullscreen API. This
1094 // allows apps which embed a WebView to retain the existing full screen
1095 // video implementation without requiring them to implement their own full
1097 if (document()->settings() && document()->settings()->fullScreenEnabled()) {
1098 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this))
1099 document()->webkitCancelFullScreen();
1101 #if ENABLE(TIZEN_FULLSCREEN_API)
1102 document()->requestFullScreenForElement(toParentMediaElement(this), Element::HARDWARE_BACKKEY_EXIT, Document::ExemptIFrameAllowFullScreenRequirement);
1104 document()->requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
1109 mediaController()->enterFullscreen();
1110 event->setDefaultHandled();
1112 HTMLInputElement::defaultEventHandler(event);
1115 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
1117 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button"));
1121 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
1123 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
1126 // ----------------------------
1128 inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document* document)
1129 : MediaControlInputElement(document, MediaUnMuteButton)
1133 PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document* document)
1135 RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(document));
1136 button->createShadowSubtree();
1137 button->setType("button");
1138 return button.release();
1141 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
1143 if (event->type() == eventNames().clickEvent) {
1144 ExceptionCode code = 0;
1145 mediaController()->setVolume(0, code);
1146 event->setDefaultHandled();
1148 HTMLInputElement::defaultEventHandler(event);
1151 const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
1153 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button"));
1157 // ----------------------------
1159 inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document* document)
1160 : MediaControlInputElement(document, MediaMuteButton)
1164 PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document* document)
1166 RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(document));
1167 button->createShadowSubtree();
1168 button->setType("button");
1169 return button.release();
1172 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
1174 if (event->type() == eventNames().clickEvent) {
1175 ExceptionCode code = 0;
1176 mediaController()->setVolume(1, code);
1177 event->setDefaultHandled();
1179 HTMLInputElement::defaultEventHandler(event);
1182 const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
1184 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button"));
1188 // ----------------------------
1190 class RenderMediaControlTimeDisplay : public RenderDeprecatedFlexibleBox {
1192 RenderMediaControlTimeDisplay(Node*);
1195 virtual void layout();
1198 RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node)
1199 : RenderDeprecatedFlexibleBox(node)
1203 // We want the timeline slider to be at least 100 pixels wide.
1204 // FIXME: Eliminate hard-coded widths altogether.
1205 static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45;
1207 void RenderMediaControlTimeDisplay::layout()
1209 RenderDeprecatedFlexibleBox::layout();
1210 RenderBox* timelineContainerBox = parentBox();
1211 while (timelineContainerBox && timelineContainerBox->isAnonymous())
1212 timelineContainerBox = timelineContainerBox->parentBox();
1214 if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays)
1218 inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document)
1219 : MediaControlElement(document)
1224 void MediaControlTimeDisplayElement::setCurrentValue(float time)
1226 m_currentValue = time;
1229 RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*)
1231 return new (arena) RenderMediaControlTimeDisplay(this);
1234 // ----------------------------
1236 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document)
1238 return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
1241 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document)
1242 : MediaControlTimeDisplayElement(document)
1246 MediaControlElementType MediaControlTimeRemainingDisplayElement::displayType() const
1248 return MediaTimeRemainingDisplay;
1251 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
1253 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display"));
1257 // ----------------------------
1259 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document)
1261 return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
1264 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document)
1265 : MediaControlTimeDisplayElement(document)
1269 MediaControlElementType MediaControlCurrentTimeDisplayElement::displayType() const
1271 return MediaCurrentTimeDisplay;
1274 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
1276 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display"));
1280 // ----------------------------
1282 #if ENABLE(VIDEO_TRACK)
1284 class RenderTextTrackContainerElement : public RenderBlock {
1286 RenderTextTrackContainerElement(Node*);
1289 virtual void layout();
1292 RenderTextTrackContainerElement::RenderTextTrackContainerElement(Node* node)
1297 void RenderTextTrackContainerElement::layout()
1299 RenderBlock::layout();
1300 if (style()->display() == NONE)
1303 ASSERT(mediaControlElementType(node()) == MediaTextTrackDisplayContainer);
1305 LayoutStateDisabler layoutStateDisabler(view());
1306 static_cast<MediaControlTextTrackContainerElement*>(node())->updateSizes();
1309 inline MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document)
1310 : MediaControlElement(document)
1315 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document)
1317 RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
1319 return element.release();
1322 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
1324 return new (arena) RenderTextTrackContainerElement(this);
1327 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
1329 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container"));
1333 void MediaControlTextTrackContainerElement::updateDisplay()
1335 HTMLMediaElement* mediaElement = toParentMediaElement(this);
1337 // 1. If the media element is an audio element, or is another playback
1338 // mechanism with no rendering area, abort these steps. There is nothing to
1340 if (!mediaElement->isVideo())
1343 // 2. Let video be the media element or other playback mechanism.
1344 HTMLVideoElement* video = static_cast<HTMLVideoElement*>(mediaElement);
1346 // 3. Let output be an empty list of absolutely positioned CSS block boxes.
1347 Vector<RefPtr<HTMLDivElement> > output;
1349 // 4. If the user agent is exposing a user interface for video, add to
1350 // output one or more completely transparent positioned CSS block boxes that
1351 // cover the same region as the user interface.
1353 // 5. If the last time these rules were run, the user agent was not exposing
1354 // a user interface for video, but now it is, let reset be true. Otherwise,
1355 // let reset be false.
1357 // There is nothing to be done explicitly for 4th and 5th steps, as
1358 // everything is handled through CSS. The caption box is on top of the
1359 // controls box, in a container set with the -webkit-box display property.
1361 // 6. Let tracks be the subset of video's list of text tracks that have as
1362 // their rules for updating the text track rendering these rules for
1363 // updating the display of WebVTT text tracks, and whose text track mode is
1364 // showing or showing by default.
1365 // 7. Let cues be an empty list of text track cues.
1366 // 8. For each track track in tracks, append to cues all the cues from
1367 // track's list of cues that have their text track cue active flag set.
1368 CueList activeCues = video->currentlyActiveCues();
1370 // 9. If reset is false, then, for each text track cue cue in cues: if cue's
1371 // text track cue display state has a set of CSS boxes, then add those boxes
1372 // to output, and remove cue from cues.
1374 // There is nothing explicitly to be done here, as all the caching occurs
1375 // within the TextTrackCue instance itself. If parameters of the cue change,
1376 // the display tree is cleared.
1378 // 10. For each text track cue cue in cues that has not yet had
1379 // corresponding CSS boxes added to output, in text track cue order, run the
1380 // following substeps:
1381 for (size_t i = 0; i < activeCues.size(); ++i) {
1382 TextTrackCue* cue = activeCues[i].data();
1384 ASSERT(cue->isActive());
1385 if (!cue->track() || !cue->track()->isRendered())
1388 RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree();
1390 if (displayBox->hasChildNodes() && !contains(static_cast<Node*>(displayBox.get())))
1391 // Note: the display tree of a cue is removed when the active flag of the cue is unset.
1392 appendChild(displayBox, ASSERT_NO_EXCEPTION, false);
1395 // 11. Return output.
1396 hasChildNodes() ? show() : hide();
1399 static const float mimimumFontSize = 16;
1400 static const float videoHeightFontSizePercentage = .05;
1401 static const float trackBottomMultiplier = .9;
1403 void MediaControlTextTrackContainerElement::updateSizes()
1405 HTMLMediaElement* mediaElement = toParentMediaElement(this);
1406 if (!mediaElement || !mediaElement->renderer() || !mediaElement->renderer()->isVideo())
1409 IntRect videoBox = toRenderVideo(mediaElement->renderer())->videoBox();
1410 if (m_videoDisplaySize == videoBox)
1412 m_videoDisplaySize = videoBox;
1414 float fontSize = m_videoDisplaySize.size().height() * videoHeightFontSizePercentage;
1415 if (fontSize != m_fontSize) {
1416 m_fontSize = fontSize;
1417 setInlineStyleProperty(CSSPropertyFontSize, String::number(fontSize) + "px");
1421 #endif // ENABLE(VIDEO_TRACK)
1423 // ----------------------------
1425 } // namespace WebCore
1427 #endif // ENABLE(VIDEO)