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 document()->didAddTouchEventHandler();
114 document()->addListenerType(Document::TOUCH_LISTENER);
115 removeInlineStyleProperty(CSSPropertyDisplay);
118 void MediaControlElement::hide()
120 document()->didRemoveTouchEventHandler();
121 setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
124 // ----------------------------
126 inline MediaControlPanelElement::MediaControlPanelElement(Document* document)
127 : MediaControlElement(document)
128 , m_canBeDragged(false)
129 , m_isBeingDragged(false)
130 , m_isDisplayed(false)
132 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
136 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document)
138 return adoptRef(new MediaControlPanelElement(document));
141 MediaControlElementType MediaControlPanelElement::displayType() const
143 return MediaControlsPanel;
146 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
148 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel"));
152 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
157 if (m_isBeingDragged)
160 RenderObject* renderer = this->renderer();
161 if (!renderer || !renderer->isBox())
164 Frame* frame = document()->frame();
168 m_dragStartEventLocation = eventLocation;
170 frame->eventHandler()->setCapturingMouseEventsNode(this);
172 m_isBeingDragged = true;
175 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
177 if (!m_isBeingDragged)
180 LayoutSize distanceDragged = eventLocation - m_dragStartEventLocation;
181 setPosition(LayoutPoint(distanceDragged.width(), distanceDragged.height()));
184 void MediaControlPanelElement::endDrag()
186 if (!m_isBeingDragged)
189 m_isBeingDragged = false;
191 Frame* frame = document()->frame();
195 frame->eventHandler()->setCapturingMouseEventsNode(0);
198 void MediaControlPanelElement::startTimer()
202 // The timer is required to set the property display:'none' on the panel,
203 // such that captions are correctly displayed at the bottom of the video
204 // at the end of the fadeout transition.
205 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
206 m_transitionTimer.startOneShot(duration);
209 void MediaControlPanelElement::stopTimer()
211 if (m_transitionTimer.isActive())
212 m_transitionTimer.stop();
216 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
224 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
226 double left = position.x();
227 double top = position.y();
229 // Set the left and top to control the panel's position; this depends on it being absolute positioned.
230 // Set the margin to zero since the position passed in will already include the effect of the margin.
231 setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
232 setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
233 setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
234 setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
236 ExceptionCode ignored;
237 classList()->add("dragged", ignored);
240 void MediaControlPanelElement::resetPosition()
242 removeInlineStyleProperty(CSSPropertyLeft);
243 removeInlineStyleProperty(CSSPropertyTop);
244 removeInlineStyleProperty(CSSPropertyMarginLeft);
245 removeInlineStyleProperty(CSSPropertyMarginTop);
247 ExceptionCode ignored;
248 classList()->remove("dragged", ignored);
251 void MediaControlPanelElement::makeOpaque()
256 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0;
258 setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
259 setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
260 setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
268 void MediaControlPanelElement::makeTransparent()
273 setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
274 setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, document()->page()->theme()->mediaControlsFadeOutDuration(), CSSPrimitiveValue::CSS_S);
275 setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
281 void MediaControlPanelElement::defaultEventHandler(Event* event)
283 MediaControlElement::defaultEventHandler(event);
285 if (event->isMouseEvent()) {
286 LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
287 if (event->type() == eventNames().mousedownEvent && event->target() == this) {
289 event->setDefaultHandled();
290 } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
291 continueDrag(location);
292 else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
293 continueDrag(location);
295 event->setDefaultHandled();
300 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
302 if (m_canBeDragged == canBeDragged)
305 m_canBeDragged = canBeDragged;
311 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
313 m_isDisplayed = isDisplayed;
316 // ----------------------------
318 inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document)
319 : MediaControlElement(document)
323 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document* document)
325 RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document));
327 return element.release();
330 MediaControlElementType MediaControlTimelineContainerElement::displayType() const
332 return MediaTimelineContainer;
335 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
337 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container"));
341 // ----------------------------
343 class RenderMediaVolumeSliderContainer : public RenderBlock {
345 RenderMediaVolumeSliderContainer(Node*);
348 virtual void layout();
351 RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node)
356 void RenderMediaVolumeSliderContainer::layout()
358 RenderBlock::layout();
360 if (style()->display() == NONE || !nextSibling() || !nextSibling()->isBox())
363 RenderBox* buttonBox = toRenderBox(nextSibling());
364 int absoluteOffsetTop = buttonBox->localToAbsolute(FloatPoint(0, -size().height())).y();
366 LayoutStateDisabler layoutStateDisabler(view());
368 // If the slider would be rendered outside the page, it should be moved below the controls.
369 if (UNLIKELY(absoluteOffsetTop < 0))
370 setY(buttonBox->offsetTop() + theme()->volumeSliderOffsetFromMuteButton(buttonBox, pixelSnappedSize()).y());
373 inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* document)
374 : MediaControlElement(document)
378 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document* document)
380 RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document));
382 return element.release();
385 RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
387 return new (arena) RenderMediaVolumeSliderContainer(this);
390 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
392 if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
395 // Poor man's mouseleave event detection.
396 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
397 if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
400 if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
406 MediaControlElementType MediaControlVolumeSliderContainerElement::displayType() const
408 return MediaVolumeSliderContainer;
411 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
413 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container"));
417 // ----------------------------
419 inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document)
420 : MediaControlElement(document)
421 , m_stateBeingDisplayed(Nothing)
425 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document* document)
427 RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(document));
429 return element.release();
432 void MediaControlStatusDisplayElement::update()
434 // Get the new state that we'll have to display.
435 StateBeingDisplayed newStateToDisplay = Nothing;
437 if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc())
438 newStateToDisplay = Loading;
439 else if (mediaController()->isLiveStream())
440 newStateToDisplay = LiveBroadcast;
442 if (newStateToDisplay == m_stateBeingDisplayed)
447 if (m_stateBeingDisplayed == Nothing)
449 else if (newStateToDisplay == Nothing)
452 m_stateBeingDisplayed = newStateToDisplay;
454 switch (m_stateBeingDisplayed) {
459 setInnerText(mediaElementLoadingStateText(), e);
462 setInnerText(mediaElementLiveBroadcastStateText(), e);
467 MediaControlElementType MediaControlStatusDisplayElement::displayType() const
469 return MediaStatusDisplay;
472 const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
474 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display"));
478 // ----------------------------
480 MediaControlInputElement::MediaControlInputElement(Document* document, MediaControlElementType displayType)
481 : HTMLInputElement(inputTag, document, 0, false)
482 , m_mediaController(0)
483 , m_displayType(displayType)
487 void MediaControlInputElement::show()
489 removeInlineStyleProperty(CSSPropertyDisplay);
492 void MediaControlInputElement::hide()
494 setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
498 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
500 if (displayType == m_displayType)
503 m_displayType = displayType;
504 if (RenderObject* object = renderer())
508 // ----------------------------
510 inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, MediaControlElementType displayType)
511 : MediaControlInputElement(document, displayType)
515 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
517 if (event->type() == eventNames().clickEvent) {
518 mediaController()->setMuted(!mediaController()->muted());
519 event->setDefaultHandled();
522 HTMLInputElement::defaultEventHandler(event);
525 void MediaControlMuteButtonElement::changedMute()
530 void MediaControlMuteButtonElement::updateDisplayType()
532 setDisplayType(mediaController()->muted() ? MediaUnMuteButton : MediaMuteButton);
535 // ----------------------------
537 inline MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls)
538 : MediaControlMuteButtonElement(document, MediaMuteButton)
539 , m_controls(controls)
543 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls)
547 RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
548 button->createShadowSubtree();
549 button->setType("button");
550 return button.release();
553 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
555 if (event->type() == eventNames().mouseoverEvent)
556 m_controls->showVolumeSlider();
558 MediaControlMuteButtonElement::defaultEventHandler(event);
561 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
563 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button"));
567 // ----------------------------
569 inline MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document)
570 : MediaControlMuteButtonElement(document, MediaMuteButton)
574 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document)
576 RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
577 button->createShadowSubtree();
578 button->setType("button");
579 return button.release();
582 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
584 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button"));
588 // ----------------------------
590 inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document)
591 : MediaControlInputElement(document, MediaPlayButton)
595 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document)
597 RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
598 button->createShadowSubtree();
599 button->setType("button");
600 return button.release();
603 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
605 if (event->type() == eventNames().clickEvent) {
606 if (mediaController()->canPlay())
607 mediaController()->play();
609 mediaController()->pause();
611 event->setDefaultHandled();
613 HTMLInputElement::defaultEventHandler(event);
616 void MediaControlPlayButtonElement::updateDisplayType()
618 setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
621 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
623 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button"));
627 // ----------------------------
629 inline MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document)
630 : MediaControlInputElement(document, MediaOverlayPlayButton)
634 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document)
636 RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
637 button->createShadowSubtree();
638 button->setType("button");
639 return button.release();
642 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
644 if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
645 mediaController()->play();
647 event->setDefaultHandled();
649 HTMLInputElement::defaultEventHandler(event);
652 void MediaControlOverlayPlayButtonElement::updateDisplayType()
654 if (mediaController()->canPlay()) {
656 setDisplayType(MediaOverlayPlayButton);
661 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
663 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button"));
667 // ----------------------------
669 inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, MediaControlElementType displayType)
670 : MediaControlInputElement(document, displayType)
671 , m_actionOnStop(Nothing)
673 , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
677 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
679 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
680 if (event->type() == eventNames().clickEvent) {
681 ExceptionCode ignoredCode;
682 float seekTime = isForwardButton() ? cSeekForwardTime : cSeekBackTime;
683 mediaController()->setCurrentTime(mediaController()->currentTime() + seekTime, ignoredCode);
684 event->setDefaultHandled();
687 // Set the mousedown and mouseup events as defaultHandled so they
688 // do not trigger drag start or end actions in MediaControlPanelElement.
689 if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
690 event->setDefaultHandled();
694 void MediaControlSeekButtonElement::setActive(bool flag, bool pause)
696 if (flag == active())
704 MediaControlInputElement::setActive(flag, pause);
707 void MediaControlSeekButtonElement::startTimer()
709 m_seekType = mediaController()->supportsScanning() ? Scan : Skip;
711 if (m_seekType == Skip) {
712 // Seeking by skipping requires the video to be paused during seeking.
713 m_actionOnStop = mediaController()->paused() ? Nothing : Play;
714 mediaController()->pause();
716 // Seeking by scanning requires the video to be playing during seeking.
717 m_actionOnStop = mediaController()->paused() ? Pause : Nothing;
718 mediaController()->play();
719 mediaController()->setPlaybackRate(nextRate());
722 m_seekTimer.start(0, m_seekType == Skip ? cSkipRepeatDelay : cScanRepeatDelay);
725 void MediaControlSeekButtonElement::stopTimer()
727 if (m_seekType == Scan)
728 mediaController()->setPlaybackRate(mediaController()->defaultPlaybackRate());
730 if (m_actionOnStop == Play)
731 mediaController()->play();
732 else if (m_actionOnStop == Pause)
733 mediaController()->pause();
735 if (m_seekTimer.isActive())
739 float MediaControlSeekButtonElement::nextRate() const
741 float rate = std::min(cScanMaximumRate, fabsf(mediaController()->playbackRate() * 2));
742 if (!isForwardButton())
747 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
749 if (m_seekType == Skip) {
751 float skipTime = isForwardButton() ? cSkipTime : -cSkipTime;
752 mediaController()->setCurrentTime(mediaController()->currentTime() + skipTime, ec);
754 mediaController()->setPlaybackRate(nextRate());
757 // ----------------------------
759 inline MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document* document)
760 : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
764 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document* document)
766 RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document));
767 button->createShadowSubtree();
768 button->setType("button");
769 return button.release();
772 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
774 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button"));
778 // ----------------------------
780 inline MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document* document)
781 : MediaControlSeekButtonElement(document, MediaSeekBackButton)
785 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document* document)
787 RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document));
788 button->createShadowSubtree();
789 button->setType("button");
790 return button.release();
793 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
795 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button"));
799 // ----------------------------
801 inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document)
802 : MediaControlInputElement(document, MediaRewindButton)
806 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document* document)
808 RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document));
809 button->createShadowSubtree();
810 button->setType("button");
811 return button.release();
814 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
816 if (event->type() == eventNames().clickEvent) {
817 ExceptionCode ignoredCode;
818 mediaController()->setCurrentTime(max(0.0f, mediaController()->currentTime() - 30), ignoredCode);
819 event->setDefaultHandled();
821 HTMLInputElement::defaultEventHandler(event);
824 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
826 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button"));
830 // ----------------------------
832 inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document)
833 : MediaControlInputElement(document, MediaReturnToRealtimeButton)
837 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document* document)
839 RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document));
840 button->createShadowSubtree();
841 button->setType("button");
843 return button.release();
846 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
848 if (event->type() == eventNames().clickEvent) {
849 mediaController()->returnToRealtime();
850 event->setDefaultHandled();
852 HTMLInputElement::defaultEventHandler(event);
855 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
857 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button"));
861 // ----------------------------
863 inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document)
864 : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
868 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document)
870 RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document));
871 button->createShadowSubtree();
872 button->setType("button");
874 return button.release();
877 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
879 if (event->type() == eventNames().clickEvent) {
880 mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
881 setChecked(mediaController()->closedCaptionsVisible());
883 event->setDefaultHandled();
886 HTMLInputElement::defaultEventHandler(event);
889 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
891 setDisplayType(mediaController()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
894 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
896 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button"));
900 // ----------------------------
902 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls)
903 : MediaControlInputElement(document, MediaSlider)
904 , m_controls(controls)
908 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls)
912 RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
913 timeline->createShadowSubtree();
914 timeline->setType("range");
915 timeline->setAttribute(precisionAttr, "float");
916 return timeline.release();
919 void MediaControlTimelineElement::defaultEventHandler(Event* event)
921 // Left button is 0. Rejects mouse events not from left button.
922 if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
928 if (event->type() == eventNames().mousedownEvent)
929 mediaController()->beginScrubbing();
931 if (event->type() == eventNames().mouseupEvent)
932 mediaController()->endScrubbing();
934 MediaControlInputElement::defaultEventHandler(event);
936 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
939 float time = narrowPrecisionToFloat(value().toDouble());
940 if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime()) {
942 mediaController()->setCurrentTime(time, ec);
945 RenderSlider* slider = toRenderSlider(renderer());
946 if (slider && slider->inDragMode())
947 m_controls->updateTimeDisplay();
950 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
958 void MediaControlTimelineElement::setPosition(float currentTime)
960 setValue(String::number(currentTime));
963 void MediaControlTimelineElement::setDuration(float duration)
965 setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
969 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
971 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline"));
975 // ----------------------------
977 inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document)
978 : MediaControlInputElement(document, MediaVolumeSlider)
979 , m_clearMutedOnUserInteraction(false)
983 PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(Document* document)
985 RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(document));
986 slider->createShadowSubtree();
987 slider->setType("range");
988 slider->setAttribute(precisionAttr, "float");
989 slider->setAttribute(maxAttr, "1");
990 return slider.release();
993 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
995 // Left button is 0. Rejects mouse events not from left button.
996 if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
1002 MediaControlInputElement::defaultEventHandler(event);
1004 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
1007 float volume = narrowPrecisionToFloat(value().toDouble());
1008 if (volume != mediaController()->volume()) {
1009 ExceptionCode ec = 0;
1010 mediaController()->setVolume(volume, ec);
1013 if (m_clearMutedOnUserInteraction)
1014 mediaController()->setMuted(false);
1017 bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents()
1022 return MediaControlInputElement::willRespondToMouseMoveEvents();
1025 bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents()
1030 return MediaControlInputElement::willRespondToMouseClickEvents();
1033 void MediaControlVolumeSliderElement::setVolume(float volume)
1035 if (value().toFloat() != volume)
1036 setValue(String::number(volume));
1039 void MediaControlVolumeSliderElement::setClearMutedOnUserInteraction(bool clearMute)
1041 m_clearMutedOnUserInteraction = clearMute;
1044 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const
1046 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider"));
1050 // ----------------------------
1052 inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document* document)
1053 : MediaControlVolumeSliderElement(document)
1057 PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document* document)
1059 RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(document));
1060 slider->createShadowSubtree();
1061 slider->setType("range");
1062 slider->setAttribute(precisionAttr, "float");
1063 slider->setAttribute(maxAttr, "1");
1064 return slider.release();
1067 const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
1069 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider"));
1073 // ----------------------------
1075 inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, MediaControls*)
1076 : MediaControlInputElement(document, MediaEnterFullscreenButton)
1080 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document, MediaControls* controls)
1084 RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document, controls));
1085 button->createShadowSubtree();
1086 button->setType("button");
1088 return button.release();
1091 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
1093 if (event->type() == eventNames().clickEvent) {
1094 #if ENABLE(FULLSCREEN_API)
1095 // Only use the new full screen API if the fullScreenEnabled setting has
1096 // been explicitly enabled. Otherwise, use the old fullscreen API. This
1097 // allows apps which embed a WebView to retain the existing full screen
1098 // video implementation without requiring them to implement their own full
1100 if (document()->settings() && document()->settings()->fullScreenEnabled()) {
1101 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this))
1102 document()->webkitCancelFullScreen();
1104 #if ENABLE(TIZEN_FULLSCREEN_API)
1105 document()->requestFullScreenForElement(toParentMediaElement(this), Element::HARDWARE_BACKKEY_EXIT, Document::ExemptIFrameAllowFullScreenRequirement);
1107 document()->requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
1112 mediaController()->enterFullscreen();
1113 event->setDefaultHandled();
1115 HTMLInputElement::defaultEventHandler(event);
1118 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
1120 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button"));
1124 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
1126 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
1129 // ----------------------------
1131 inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document* document)
1132 : MediaControlInputElement(document, MediaUnMuteButton)
1136 PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document* document)
1138 RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(document));
1139 button->createShadowSubtree();
1140 button->setType("button");
1141 return button.release();
1144 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
1146 if (event->type() == eventNames().clickEvent) {
1147 ExceptionCode code = 0;
1148 mediaController()->setVolume(0, code);
1149 event->setDefaultHandled();
1151 HTMLInputElement::defaultEventHandler(event);
1154 const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
1156 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button"));
1160 // ----------------------------
1162 inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document* document)
1163 : MediaControlInputElement(document, MediaMuteButton)
1167 PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document* document)
1169 RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(document));
1170 button->createShadowSubtree();
1171 button->setType("button");
1172 return button.release();
1175 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
1177 if (event->type() == eventNames().clickEvent) {
1178 ExceptionCode code = 0;
1179 mediaController()->setVolume(1, code);
1180 event->setDefaultHandled();
1182 HTMLInputElement::defaultEventHandler(event);
1185 const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
1187 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button"));
1191 // ----------------------------
1193 class RenderMediaControlTimeDisplay : public RenderDeprecatedFlexibleBox {
1195 RenderMediaControlTimeDisplay(Node*);
1198 virtual void layout();
1201 RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node)
1202 : RenderDeprecatedFlexibleBox(node)
1206 // We want the timeline slider to be at least 100 pixels wide.
1207 // FIXME: Eliminate hard-coded widths altogether.
1208 static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45;
1210 void RenderMediaControlTimeDisplay::layout()
1212 RenderDeprecatedFlexibleBox::layout();
1213 RenderBox* timelineContainerBox = parentBox();
1214 while (timelineContainerBox && timelineContainerBox->isAnonymous())
1215 timelineContainerBox = timelineContainerBox->parentBox();
1217 if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays)
1221 inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document)
1222 : MediaControlElement(document)
1227 void MediaControlTimeDisplayElement::setCurrentValue(float time)
1229 m_currentValue = time;
1232 RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*)
1234 return new (arena) RenderMediaControlTimeDisplay(this);
1237 // ----------------------------
1239 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document)
1241 return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
1244 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document)
1245 : MediaControlTimeDisplayElement(document)
1249 MediaControlElementType MediaControlTimeRemainingDisplayElement::displayType() const
1251 return MediaTimeRemainingDisplay;
1254 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
1256 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display"));
1260 // ----------------------------
1262 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document)
1264 return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
1267 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document)
1268 : MediaControlTimeDisplayElement(document)
1272 MediaControlElementType MediaControlCurrentTimeDisplayElement::displayType() const
1274 return MediaCurrentTimeDisplay;
1277 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
1279 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display"));
1283 // ----------------------------
1285 #if ENABLE(VIDEO_TRACK)
1287 class RenderTextTrackContainerElement : public RenderBlock {
1289 RenderTextTrackContainerElement(Node*);
1292 virtual void layout();
1295 RenderTextTrackContainerElement::RenderTextTrackContainerElement(Node* node)
1300 void RenderTextTrackContainerElement::layout()
1302 RenderBlock::layout();
1303 if (style()->display() == NONE)
1306 ASSERT(mediaControlElementType(node()) == MediaTextTrackDisplayContainer);
1308 LayoutStateDisabler layoutStateDisabler(view());
1309 static_cast<MediaControlTextTrackContainerElement*>(node())->updateSizes();
1312 inline MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document)
1313 : MediaControlElement(document)
1318 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document)
1320 RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
1322 return element.release();
1325 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
1327 return new (arena) RenderTextTrackContainerElement(this);
1330 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
1332 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container"));
1336 void MediaControlTextTrackContainerElement::updateDisplay()
1338 HTMLMediaElement* mediaElement = toParentMediaElement(this);
1340 // 1. If the media element is an audio element, or is another playback
1341 // mechanism with no rendering area, abort these steps. There is nothing to
1343 if (!mediaElement->isVideo())
1346 // 2. Let video be the media element or other playback mechanism.
1347 HTMLVideoElement* video = static_cast<HTMLVideoElement*>(mediaElement);
1349 // 3. Let output be an empty list of absolutely positioned CSS block boxes.
1350 Vector<RefPtr<HTMLDivElement> > output;
1352 // 4. If the user agent is exposing a user interface for video, add to
1353 // output one or more completely transparent positioned CSS block boxes that
1354 // cover the same region as the user interface.
1356 // 5. If the last time these rules were run, the user agent was not exposing
1357 // a user interface for video, but now it is, let reset be true. Otherwise,
1358 // let reset be false.
1360 // There is nothing to be done explicitly for 4th and 5th steps, as
1361 // everything is handled through CSS. The caption box is on top of the
1362 // controls box, in a container set with the -webkit-box display property.
1364 // 6. Let tracks be the subset of video's list of text tracks that have as
1365 // their rules for updating the text track rendering these rules for
1366 // updating the display of WebVTT text tracks, and whose text track mode is
1367 // showing or showing by default.
1368 // 7. Let cues be an empty list of text track cues.
1369 // 8. For each track track in tracks, append to cues all the cues from
1370 // track's list of cues that have their text track cue active flag set.
1371 CueList activeCues = video->currentlyActiveCues();
1373 // 9. If reset is false, then, for each text track cue cue in cues: if cue's
1374 // text track cue display state has a set of CSS boxes, then add those boxes
1375 // to output, and remove cue from cues.
1377 // There is nothing explicitly to be done here, as all the caching occurs
1378 // within the TextTrackCue instance itself. If parameters of the cue change,
1379 // the display tree is cleared.
1381 // 10. For each text track cue cue in cues that has not yet had
1382 // corresponding CSS boxes added to output, in text track cue order, run the
1383 // following substeps:
1384 for (size_t i = 0; i < activeCues.size(); ++i) {
1385 TextTrackCue* cue = activeCues[i].data();
1387 ASSERT(cue->isActive());
1388 if (!cue->track() || !cue->track()->isRendered())
1391 RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree();
1393 if (displayBox->hasChildNodes() && !contains(static_cast<Node*>(displayBox.get())))
1394 // Note: the display tree of a cue is removed when the active flag of the cue is unset.
1395 appendChild(displayBox, ASSERT_NO_EXCEPTION, false);
1398 // 11. Return output.
1399 hasChildNodes() ? show() : hide();
1402 static const float mimimumFontSize = 16;
1403 static const float videoHeightFontSizePercentage = .05;
1404 static const float trackBottomMultiplier = .9;
1406 void MediaControlTextTrackContainerElement::updateSizes()
1408 HTMLMediaElement* mediaElement = toParentMediaElement(this);
1409 if (!mediaElement || !mediaElement->renderer() || !mediaElement->renderer()->isVideo())
1412 IntRect videoBox = toRenderVideo(mediaElement->renderer())->videoBox();
1413 if (m_videoDisplaySize == videoBox)
1415 m_videoDisplaySize = videoBox;
1417 float fontSize = m_videoDisplaySize.size().height() * videoHeightFontSizePercentage;
1418 if (fontSize != m_fontSize) {
1419 m_fontSize = fontSize;
1420 setInlineStyleProperty(CSSPropertyFontSize, String::number(fontSize) + "px");
1424 #endif // ENABLE(VIDEO_TRACK)
1426 // ----------------------------
1428 } // namespace WebCore
1430 #endif // ENABLE(VIDEO)