1/*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29
30#if ENABLE(VIDEO)
31#include "MediaControlsBlackBerry.h"
32
33#include "Chrome.h"
34#include "DOMTokenList.h"
35#include "ExceptionCodePlaceholder.h"
36#include "Frame.h"
37#include "HTMLMediaElement.h"
38#include "HTMLNames.h"
39#include "MediaControlElements.h"
40#include "MouseEvent.h"
41#include "Page.h"
42#include "RenderDeprecatedFlexibleBox.h"
43#include "RenderSlider.h"
44#include "RenderTheme.h"
45#include "Settings.h"
46#include "Text.h"
47
48#if ENABLE(VIDEO_TRACK)
49#include "TextTrackCue.h"
50#endif
51
52using namespace std;
53
54namespace WebCore {
55
56using namespace HTMLNames;
57
58static const double timeWithoutMouseMovementBeforeHidingControls = 3;
59
60inline MediaControlButtonGroupContainerElement::MediaControlButtonGroupContainerElement(Document* document)
61    : MediaControlDivElement(document, MediaControlsPanel)
62{
63}
64
65PassRefPtr<MediaControlButtonGroupContainerElement> MediaControlButtonGroupContainerElement::create(Document* document)
66{
67    RefPtr<MediaControlButtonGroupContainerElement> element = adoptRef(new MediaControlButtonGroupContainerElement(document));
68    return element.release();
69}
70
71const AtomicString& MediaControlButtonGroupContainerElement::shadowPseudoId() const
72{
73    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-button-group-container", AtomicString::ConstructFromLiteral));
74    return id;
75}
76
77inline MediaControlTimeDisplayContainerElement::MediaControlTimeDisplayContainerElement(Document* document)
78    : MediaControlDivElement(document, MediaControlsPanel)
79{
80}
81
82PassRefPtr<MediaControlTimeDisplayContainerElement> MediaControlTimeDisplayContainerElement::create(Document* document)
83{
84    RefPtr<MediaControlTimeDisplayContainerElement> element = adoptRef(new MediaControlTimeDisplayContainerElement(document));
85    return element.release();
86}
87
88const AtomicString& MediaControlTimeDisplayContainerElement::shadowPseudoId() const
89{
90    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-display-container", AtomicString::ConstructFromLiteral));
91    return id;
92}
93
94MediaControlEmbeddedPanelElement::MediaControlEmbeddedPanelElement(Document* document)
95    : MediaControlDivElement(document, MediaControlsPanel)
96    , m_canBeDragged(false)
97    , m_isBeingDragged(false)
98    , m_isDisplayed(false)
99    , m_opaque(true)
100    , m_transitionTimer(this, &MediaControlEmbeddedPanelElement::transitionTimerFired)
101{
102}
103
104PassRefPtr<MediaControlEmbeddedPanelElement> MediaControlEmbeddedPanelElement::create(Document* document)
105{
106    return adoptRef(new MediaControlEmbeddedPanelElement(document));
107}
108
109const AtomicString& MediaControlEmbeddedPanelElement::shadowPseudoId() const
110{
111    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-embedded-panel", AtomicString::ConstructFromLiteral));
112    return id;
113}
114
115void MediaControlEmbeddedPanelElement::startDrag(const LayoutPoint& eventLocation)
116{
117    if (!m_canBeDragged)
118        return;
119
120    if (m_isBeingDragged)
121        return;
122
123    RenderObject* renderer = this->renderer();
124    if (!renderer || !renderer->isBox())
125        return;
126
127    Frame* frame = document()->frame();
128    if (!frame)
129        return;
130
131    m_lastDragEventLocation = eventLocation;
132
133    frame->eventHandler()->setCapturingMouseEventsNode(this);
134
135    m_isBeingDragged = true;
136}
137
138void MediaControlEmbeddedPanelElement::continueDrag(const LayoutPoint& eventLocation)
139{
140    if (!m_isBeingDragged)
141        return;
142
143    LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation;
144    m_cumulativeDragOffset.move(distanceDragged);
145    m_lastDragEventLocation = eventLocation;
146    setPosition(m_cumulativeDragOffset);
147}
148
149void MediaControlEmbeddedPanelElement::endDrag()
150{
151    if (!m_isBeingDragged)
152        return;
153
154    m_isBeingDragged = false;
155
156    Frame* frame = document()->frame();
157    if (!frame)
158        return;
159
160    frame->eventHandler()->setCapturingMouseEventsNode(0);
161}
162
163void MediaControlEmbeddedPanelElement::startTimer()
164{
165    stopTimer();
166
167    // The timer is required to set the property display:'none' on the panel,
168    // such that captions are correctly displayed at the bottom of the video
169    // at the end of the fadeout transition.
170    double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
171    m_transitionTimer.startOneShot(duration);
172}
173
174void MediaControlEmbeddedPanelElement::stopTimer()
175{
176    if (m_transitionTimer.isActive())
177        m_transitionTimer.stop();
178}
179
180void MediaControlEmbeddedPanelElement::transitionTimerFired(Timer<MediaControlEmbeddedPanelElement>*)
181{
182    if (!m_opaque)
183        hide();
184
185    stopTimer();
186}
187
188void MediaControlEmbeddedPanelElement::setPosition(const LayoutPoint& position)
189{
190    double left = position.x();
191    double top = position.y();
192
193    // Set the left and top to control the panel's position; this depends on it being absolute positioned.
194    // Set the margin to zero since the position passed in will already include the effect of the margin.
195    setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
196    setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
197    setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
198    setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
199
200    classList()->add("dragged", IGNORE_EXCEPTION);
201}
202
203void MediaControlEmbeddedPanelElement::resetPosition()
204{
205    removeInlineStyleProperty(CSSPropertyLeft);
206    removeInlineStyleProperty(CSSPropertyTop);
207    removeInlineStyleProperty(CSSPropertyMarginLeft);
208    removeInlineStyleProperty(CSSPropertyMarginTop);
209
210    classList()->remove("dragged", IGNORE_EXCEPTION);
211
212    m_cumulativeDragOffset.setX(0);
213    m_cumulativeDragOffset.setY(0);
214}
215
216void MediaControlEmbeddedPanelElement::makeOpaque()
217{
218    if (m_opaque)
219        return;
220
221    double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0;
222
223    setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
224    setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
225    setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
226
227    m_opaque = true;
228
229    if (m_isDisplayed)
230        show();
231}
232
233void MediaControlEmbeddedPanelElement::makeTransparent()
234{
235    if (!m_opaque)
236        return;
237
238    double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
239
240    setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
241    setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
242    setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
243
244    m_opaque = false;
245    startTimer();
246}
247
248void MediaControlEmbeddedPanelElement::defaultEventHandler(Event* event)
249{
250    MediaControlDivElement::defaultEventHandler(event);
251
252    if (event->isMouseEvent()) {
253        LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
254        if (event->type() == eventNames().mousedownEvent && event->target() == this) {
255            startDrag(location);
256            event->setDefaultHandled();
257        } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
258            continueDrag(location);
259        else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
260            continueDrag(location);
261            endDrag();
262            event->setDefaultHandled();
263        }
264    }
265}
266
267void MediaControlEmbeddedPanelElement::setCanBeDragged(bool canBeDragged)
268{
269    if (m_canBeDragged == canBeDragged)
270        return;
271
272    m_canBeDragged = canBeDragged;
273
274    if (!canBeDragged)
275        endDrag();
276}
277
278void MediaControlEmbeddedPanelElement::setIsDisplayed(bool isDisplayed)
279{
280    m_isDisplayed = isDisplayed;
281}
282
283inline MediaControlFullscreenTimeDisplayContainerElement::MediaControlFullscreenTimeDisplayContainerElement(Document* document)
284    : MediaControlDivElement(document, MediaControlsPanel)
285{
286}
287
288PassRefPtr<MediaControlFullscreenTimeDisplayContainerElement> MediaControlFullscreenTimeDisplayContainerElement::create(Document* document)
289{
290    RefPtr<MediaControlFullscreenTimeDisplayContainerElement> element = adoptRef(new MediaControlFullscreenTimeDisplayContainerElement(document));
291    return element.release();
292}
293
294const AtomicString& MediaControlFullscreenTimeDisplayContainerElement::shadowPseudoId() const
295{
296    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-time-display-container", AtomicString::ConstructFromLiteral));
297    return id;
298}
299
300inline MediaControlFullscreenButtonContainerElement::MediaControlFullscreenButtonContainerElement(Document* document)
301    : MediaControlDivElement(document, MediaControlsPanel)
302{
303}
304
305PassRefPtr<MediaControlFullscreenButtonContainerElement> MediaControlFullscreenButtonContainerElement::create(Document* document)
306{
307    RefPtr<MediaControlFullscreenButtonContainerElement> element = adoptRef(new MediaControlFullscreenButtonContainerElement(document));
308    return element.release();
309}
310
311const AtomicString& MediaControlFullscreenButtonContainerElement::shadowPseudoId() const
312{
313    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button-container", AtomicString::ConstructFromLiteral));
314    return id;
315}
316
317inline MediaControlFullscreenButtonDividerElement::MediaControlFullscreenButtonDividerElement(Document* document)
318    : MediaControlDivElement(document, MediaRewindButton)
319{
320}
321
322PassRefPtr<MediaControlFullscreenButtonDividerElement> MediaControlFullscreenButtonDividerElement::create(Document* document)
323{
324    RefPtr<MediaControlFullscreenButtonDividerElement> element = adoptRef(new MediaControlFullscreenButtonDividerElement(document));
325    return element.release();
326}
327
328const AtomicString& MediaControlFullscreenButtonDividerElement::shadowPseudoId() const
329{
330    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button-divider", AtomicString::ConstructFromLiteral));
331    return id;
332}
333
334inline MediaControlPlayButtonContainerElement::MediaControlPlayButtonContainerElement(Document* document)
335    : MediaControlDivElement(document, MediaControlsPanel)
336{
337}
338
339PassRefPtr<MediaControlPlayButtonContainerElement> MediaControlPlayButtonContainerElement::create(Document* document)
340{
341    RefPtr<MediaControlPlayButtonContainerElement> element = adoptRef(new MediaControlPlayButtonContainerElement(document));
342    return element.release();
343}
344
345const AtomicString& MediaControlPlayButtonContainerElement::shadowPseudoId() const
346{
347    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button-container", AtomicString::ConstructFromLiteral));
348    return id;
349}
350
351inline MediaControlPlaceholderElement::MediaControlPlaceholderElement(Document* document)
352    : MediaControlDivElement(document, MediaControlsPanel)
353{
354}
355
356PassRefPtr<MediaControlPlaceholderElement> MediaControlPlaceholderElement::create(Document* document)
357{
358    RefPtr<MediaControlPlaceholderElement> element = adoptRef(new MediaControlPlaceholderElement(document));
359    return element.release();
360}
361
362const AtomicString& MediaControlPlaceholderElement::shadowPseudoId() const
363{
364    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-placeholder", AtomicString::ConstructFromLiteral));
365    return id;
366}
367
368inline MediaControlFullscreenPlayButtonElement::MediaControlFullscreenPlayButtonElement(Document* document)
369    : MediaControlInputElement(document, MediaPlayButton)
370{
371}
372
373PassRefPtr<MediaControlFullscreenPlayButtonElement> MediaControlFullscreenPlayButtonElement::create(Document* document)
374{
375    RefPtr<MediaControlFullscreenPlayButtonElement> button = adoptRef(new MediaControlFullscreenPlayButtonElement(document));
376    button->ensureUserAgentShadowRoot();
377    button->setType("button");
378    return button.release();
379}
380
381void MediaControlFullscreenPlayButtonElement::defaultEventHandler(Event* event)
382{
383    if (event->type() == eventNames().clickEvent) {
384        if (mediaController()->canPlay())
385            mediaController()->play();
386        else
387            mediaController()->pause();
388        updateDisplayType();
389        event->setDefaultHandled();
390    }
391    HTMLInputElement::defaultEventHandler(event);
392}
393
394void MediaControlFullscreenPlayButtonElement::updateDisplayType()
395{
396    setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
397}
398
399const AtomicString& MediaControlFullscreenPlayButtonElement::shadowPseudoId() const
400{
401    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-play-button", AtomicString::ConstructFromLiteral));
402    return id;
403}
404
405inline MediaControlFullscreenFullscreenButtonElement::MediaControlFullscreenFullscreenButtonElement(Document* document)
406    : MediaControlInputElement(document, MediaExitFullscreenButton)
407{
408}
409
410PassRefPtr<MediaControlFullscreenFullscreenButtonElement> MediaControlFullscreenFullscreenButtonElement::create(Document* document)
411{
412    RefPtr<MediaControlFullscreenFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenFullscreenButtonElement(document));
413    button->ensureUserAgentShadowRoot();
414    button->setType("button");
415    button->hide();
416    return button.release();
417}
418
419void MediaControlFullscreenFullscreenButtonElement::defaultEventHandler(Event* event)
420{
421    if (event->type() == eventNames().clickEvent) {
422#if ENABLE(FULLSCREEN_API)
423        // Only use the new full screen API if the fullScreenEnabled setting has
424        // been explicitly enabled. Otherwise, use the old fullscreen API. This
425        // allows apps which embed a WebView to retain the existing full screen
426        // video implementation without requiring them to implement their own full
427        // screen behavior.
428        if (document()->settings() && document()->settings()->fullScreenEnabled()) {
429            if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this))
430                document()->webkitCancelFullScreen();
431            else
432                document()->requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
433        } else
434#endif
435            mediaController()->enterFullscreen();
436        event->setDefaultHandled();
437    }
438    HTMLInputElement::defaultEventHandler(event);
439}
440
441const AtomicString& MediaControlFullscreenFullscreenButtonElement::shadowPseudoId() const
442{
443    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-fullscreen-button", AtomicString::ConstructFromLiteral));
444    return id;
445}
446
447void MediaControlFullscreenFullscreenButtonElement::setIsFullscreen(bool)
448{
449    setDisplayType(MediaExitFullscreenButton);
450}
451
452inline MediaControlFullscreenTimelineContainerElement::MediaControlFullscreenTimelineContainerElement(Document* document)
453    : MediaControlDivElement(document, MediaTimelineContainer)
454{
455}
456
457PassRefPtr<MediaControlFullscreenTimelineContainerElement> MediaControlFullscreenTimelineContainerElement::create(Document* document)
458{
459    RefPtr<MediaControlFullscreenTimelineContainerElement> element = adoptRef(new MediaControlFullscreenTimelineContainerElement(document));
460    element->hide();
461    return element.release();
462}
463
464const AtomicString& MediaControlFullscreenTimelineContainerElement::shadowPseudoId() const
465{
466    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-timeline-container", AtomicString::ConstructFromLiteral));
467    return id;
468}
469
470MediaControlFullscreenTimelineElement::MediaControlFullscreenTimelineElement(Document* document, MediaControls* controls)
471    : MediaControlInputElement(document, MediaSlider)
472    , m_controls(controls)
473{
474}
475
476PassRefPtr<MediaControlFullscreenTimelineElement> MediaControlFullscreenTimelineElement::create(Document* document, MediaControls* controls)
477{
478    ASSERT(controls);
479
480    RefPtr<MediaControlFullscreenTimelineElement> timeline = adoptRef(new MediaControlFullscreenTimelineElement(document, controls));
481    timeline->ensureUserAgentShadowRoot();
482    timeline->setType("range");
483    timeline->setAttribute(precisionAttr, "double");
484    return timeline.release();
485}
486
487void MediaControlFullscreenTimelineElement::defaultEventHandler(Event* event)
488{
489    // Left button is 0. Rejects mouse events not from left button.
490    if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
491        return;
492
493    if (!attached())
494        return;
495
496    if (event->type() == eventNames().mousedownEvent)
497        mediaController()->beginScrubbing();
498
499    if (event->type() == eventNames().mouseupEvent)
500        mediaController()->endScrubbing();
501
502    MediaControlInputElement::defaultEventHandler(event);
503
504    if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
505        return;
506
507    double time = value().toDouble();
508    if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime())
509        mediaController()->setCurrentTime(time, IGNORE_EXCEPTION);
510
511    RenderSlider* slider = toRenderSlider(renderer());
512    if (slider && slider->inDragMode())
513        m_controls->updateCurrentTimeDisplay();
514}
515
516bool MediaControlFullscreenTimelineElement::willRespondToMouseClickEvents()
517{
518    if (!attached())
519        return false;
520
521    return true;
522}
523
524void MediaControlFullscreenTimelineElement::setPosition(double currentTime)
525{
526    setValue(String::number(currentTime));
527}
528
529void MediaControlFullscreenTimelineElement::setDuration(double duration)
530{
531    setAttribute(maxAttr, String::number(std::isfinite(duration) ? duration : 0));
532}
533
534const AtomicString& MediaControlFullscreenTimelineElement::shadowPseudoId() const
535{
536    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-timeline", AtomicString::ConstructFromLiteral));
537    return id;
538}
539
540PassRefPtr<MediaControlFullscreenTimeRemainingDisplayElement> MediaControlFullscreenTimeRemainingDisplayElement::create(Document* document)
541{
542    return adoptRef(new MediaControlFullscreenTimeRemainingDisplayElement(document));
543}
544
545MediaControlFullscreenTimeRemainingDisplayElement::MediaControlFullscreenTimeRemainingDisplayElement(Document* document)
546    : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay)
547{
548}
549
550const AtomicString& MediaControlFullscreenTimeRemainingDisplayElement::shadowPseudoId() const
551{
552    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-time-remaining-display", AtomicString::ConstructFromLiteral));
553    return id;
554}
555
556PassRefPtr<MediaControlFullscreenCurrentTimeDisplayElement> MediaControlFullscreenCurrentTimeDisplayElement::create(Document* document)
557{
558    return adoptRef(new MediaControlFullscreenCurrentTimeDisplayElement(document));
559}
560
561MediaControlFullscreenCurrentTimeDisplayElement::MediaControlFullscreenCurrentTimeDisplayElement(Document* document)
562    : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay)
563{
564}
565
566const AtomicString& MediaControlFullscreenCurrentTimeDisplayElement::shadowPseudoId() const
567{
568    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-current-time-display", AtomicString::ConstructFromLiteral));
569    return id;
570}
571
572MediaControlAudioMuteButtonElement::MediaControlAudioMuteButtonElement(Document* document, MediaControls* controls)
573    : MediaControlMuteButtonElement(document, MediaMuteButton)
574    , m_controls(controls)
575{
576}
577
578PassRefPtr<MediaControlAudioMuteButtonElement> MediaControlAudioMuteButtonElement::create(Document* document, MediaControls* controls)
579{
580    ASSERT(controls);
581
582    RefPtr<MediaControlAudioMuteButtonElement> button = adoptRef(new MediaControlAudioMuteButtonElement(document, controls));
583    button->ensureUserAgentShadowRoot();
584    button->setType("button");
585    return button.release();
586}
587
588void MediaControlAudioMuteButtonElement::defaultEventHandler(Event* event)
589{
590    if (event->type() == eventNames().mousedownEvent) {
591        // We do not mute when the media player volume/mute control is touched.
592        // Instead we show/hide the volume slider.
593        static_cast<MediaControlsBlackBerry*>(m_controls)->toggleVolumeSlider();
594        event->setDefaultHandled();
595        return;
596    }
597    if (event->type() == eventNames().mouseoverEvent)
598        m_controls->showVolumeSlider();
599
600    MediaControlMuteButtonElement::defaultEventHandler(event);
601}
602
603const AtomicString& MediaControlAudioMuteButtonElement::shadowPseudoId() const
604{
605    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-audio-mute-button", AtomicString::ConstructFromLiteral));
606    return id;
607}
608
609MediaControlsBlackBerry::MediaControlsBlackBerry(Document* document)
610    : MediaControls(document)
611    , m_buttonContainer(0)
612    , m_timeDisplayContainer(0)
613    , m_fullscreenTimeDisplayContainer(0)
614    , m_fullscreenPlayButton(0)
615    , m_fullscreenCurrentTimeDisplay(0)
616    , m_fullscreenTimeline(0)
617    , m_timeRemainingDisplay(0)
618    , m_fullscreenTimeRemainingDisplay(0)
619    , m_timelineContainer(0)
620    , m_fullscreenTimelineContainer(0)
621    , m_fullScreenDivider(0)
622    , m_fullscreenFullScreenButton(0)
623    , m_muteButton(0)
624    , m_volumeSliderContainer(0)
625    , m_embeddedPanel(0)
626    , m_fullScreenButtonContainer(0)
627    , m_playButtonContainer(0)
628    , m_placeholder(0)
629{
630}
631
632PassRefPtr<MediaControls> MediaControls::create(Document* document)
633{
634    return MediaControlsBlackBerry::createControls(document);
635}
636
637PassRefPtr<MediaControlsBlackBerry> MediaControlsBlackBerry::createControls(Document* document)
638{
639    if (!document->page())
640        return 0;
641
642    RefPtr<MediaControlsBlackBerry> controls = adoptRef(new MediaControlsBlackBerry(document));
643
644    RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document);
645    RefPtr<MediaControlEmbeddedPanelElement> embedPanel = MediaControlEmbeddedPanelElement::create(document);
646
647    ExceptionCode ec;
648
649    RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(document);
650    controls->m_playButton = playButton.get();
651    embedPanel->appendChild(playButton.release(), ec, AttachLazily);
652    if (ec)
653        return 0;
654
655    RefPtr<MediaControlTimelineContainerElement> timelineContainer = MediaControlTimelineContainerElement::create(document);
656
657    RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(document, controls.get());
658    controls->m_timeline = timeline.get();
659    timelineContainer->appendChild(timeline.release(), ec, AttachLazily);
660    if (ec)
661        return 0;
662
663    RefPtr<MediaControlTimeDisplayContainerElement> timeDisplayContainer = MediaControlTimeDisplayContainerElement::create(document);
664
665    RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document);
666    controls->m_currentTimeDisplay = currentTimeDisplay.get();
667    timeDisplayContainer->appendChild(currentTimeDisplay.release(), ec, AttachLazily);
668    if (ec)
669        return 0;
670
671    RefPtr<MediaControlTimeRemainingDisplayElement> timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(document);
672    controls->m_timeRemainingDisplay = timeRemainingDisplay.get();
673    timeDisplayContainer->appendChild(timeRemainingDisplay.release(), ec, AttachLazily);
674    if (ec)
675        return 0;
676
677    controls->m_timeDisplayContainer = timeDisplayContainer.get();
678    timelineContainer->appendChild(timeDisplayContainer.release(), ec, AttachLazily);
679    if (ec)
680        return 0;
681
682    controls->m_timelineContainer = timelineContainer.get();
683    embedPanel->appendChild(timelineContainer.release(), ec, AttachLazily);
684    if (ec)
685        return 0;
686
687    RefPtr<MediaControlFullscreenButtonElement> fullScreenButton = MediaControlFullscreenButtonElement::create(document);
688    controls->m_fullScreenButton = fullScreenButton.get();
689    embedPanel->appendChild(fullScreenButton.release(), ec, AttachLazily);
690    if (ec)
691        return 0;
692
693    if (document->page()->theme()->usesMediaControlVolumeSlider()) {
694        // The mute button and the slider element should be in the same div.
695        RefPtr<HTMLDivElement> volumeControlContainer = HTMLDivElement::create(document);
696
697        RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(document);
698
699        RefPtr<MediaControlPanelVolumeSliderElement> slider = MediaControlPanelVolumeSliderElement::create(document);
700        controls->m_volumeSlider = slider.get();
701        volumeSliderContainer->appendChild(slider.release(), ec, AttachLazily);
702        if (ec)
703            return 0;
704
705        controls->m_volumeSliderContainer = volumeSliderContainer.get();
706        volumeControlContainer->appendChild(volumeSliderContainer.release(), ec, AttachLazily);
707        if (ec)
708            return 0;
709        RefPtr<MediaControlAudioMuteButtonElement> muteButton = MediaControlAudioMuteButtonElement::create(document, controls.get());
710        controls->m_muteButton = muteButton.get();
711        volumeControlContainer->appendChild(muteButton.release(), ec, AttachLazily);
712        if (ec)
713            return 0;
714
715        embedPanel->appendChild(volumeControlContainer.release(), ec, AttachLazily);
716        if (ec)
717            return 0;
718    }
719
720    RefPtr<MediaControlFullscreenTimelineContainerElement> fullscreenTimelineContainer = MediaControlFullscreenTimelineContainerElement::create(document);
721
722    RefPtr<MediaControlFullscreenTimelineElement> fullscreenTimeline = MediaControlFullscreenTimelineElement::create(document, controls.get());
723    controls->m_fullscreenTimeline = fullscreenTimeline.get();
724    fullscreenTimelineContainer->appendChild(fullscreenTimeline.release(), ec, AttachLazily);
725    if (ec)
726        return 0;
727
728    RefPtr<MediaControlFullscreenTimeDisplayContainerElement> fullscreenTimeDisplayContainer = MediaControlFullscreenTimeDisplayContainerElement::create(document);
729
730    RefPtr<MediaControlFullscreenCurrentTimeDisplayElement> fullscreenCurrentTimeDisplay = MediaControlFullscreenCurrentTimeDisplayElement::create(document);
731    controls->m_fullscreenCurrentTimeDisplay = fullscreenCurrentTimeDisplay.get();
732    fullscreenTimeDisplayContainer->appendChild(fullscreenCurrentTimeDisplay.release(), ec, AttachLazily);
733    if (ec)
734        return 0;
735
736    RefPtr<MediaControlFullscreenTimeRemainingDisplayElement> fullscreenTimeRemainingDisplay = MediaControlFullscreenTimeRemainingDisplayElement::create(document);
737    controls->m_fullscreenTimeRemainingDisplay = fullscreenTimeRemainingDisplay.get();
738    fullscreenTimeDisplayContainer->appendChild(fullscreenTimeRemainingDisplay.release(), ec, AttachLazily);
739    if (ec)
740        return 0;
741
742    controls->m_fullscreenTimeDisplayContainer = fullscreenTimeDisplayContainer.get();
743    fullscreenTimelineContainer->appendChild(fullscreenTimeDisplayContainer.release(), ec, AttachLazily);
744    if (ec)
745        return 0;
746
747    controls->m_fullscreenTimelineContainer = fullscreenTimelineContainer.get();
748    panel->appendChild(fullscreenTimelineContainer.release(), ec, AttachLazily);
749    if (ec)
750        return 0;
751
752    RefPtr<MediaControlButtonGroupContainerElement> buttonGroupContainer = MediaControlButtonGroupContainerElement::create(document);
753
754    // FIXME: Only create when needed <http://webkit.org/b/57163>
755    RefPtr<MediaControlFullscreenButtonContainerElement> fullScreenButtonContainer = MediaControlFullscreenButtonContainerElement::create(document);
756    controls->m_fullScreenButtonContainer = fullScreenButtonContainer.get();
757    RefPtr<MediaControlFullscreenFullscreenButtonElement> fullscreenFullScreenButton = MediaControlFullscreenFullscreenButtonElement::create(document);
758    controls->m_fullscreenFullScreenButton = fullscreenFullScreenButton.get();
759    fullScreenButtonContainer->appendChild(fullscreenFullScreenButton.release(), ec, AttachLazily);
760    if (ec)
761        return 0;
762    RefPtr<MediaControlFullscreenButtonDividerElement> fullScreenDivider = MediaControlFullscreenButtonDividerElement::create(document);
763    controls->m_fullScreenDivider = fullScreenDivider.get();
764    fullScreenButtonContainer->appendChild(fullScreenDivider.release(), ec, AttachLazily);
765    if (ec)
766        return 0;
767    buttonGroupContainer->appendChild(fullScreenButtonContainer.release(), ec, AttachLazily);
768    if (ec)
769        return 0;
770
771    RefPtr<MediaControlPlayButtonContainerElement> playButtonContainer = MediaControlPlayButtonContainerElement::create(document);
772    controls->m_playButtonContainer = playButtonContainer.get();
773    RefPtr<MediaControlFullscreenPlayButtonElement> fullscreenPlayButton = MediaControlFullscreenPlayButtonElement::create(document);
774    controls->m_fullscreenPlayButton = fullscreenPlayButton.get();
775    playButtonContainer->appendChild(fullscreenPlayButton.release(), ec, AttachLazily);
776    if (ec)
777        return 0;
778    buttonGroupContainer->appendChild(playButtonContainer.release(), ec, AttachLazily);
779    if (ec)
780        return 0;
781
782    RefPtr<MediaControlPlaceholderElement> placeholder = MediaControlPlaceholderElement::create(document);
783    controls->m_placeholder = placeholder.get();
784    buttonGroupContainer->appendChild(placeholder.release(), ec, AttachLazily);
785    if (ec)
786        return 0;
787
788    controls->m_buttonContainer = buttonGroupContainer.get();
789    panel->appendChild(buttonGroupContainer.release(), ec, AttachLazily);
790    if (ec)
791        return 0;
792
793    if (document->page()->theme()->supportsClosedCaptioning()) {
794        RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, controls.get());
795        controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
796        panel->appendChild(toggleClosedCaptionsButton.release(), ec, AttachLazily);
797        if (ec)
798            return 0;
799    }
800
801    controls->m_panel = panel.get();
802    controls->appendChild(panel.release(), ec, AttachLazily);
803    if (ec)
804        return 0;
805
806    controls->m_embeddedPanel = embedPanel.get();
807    controls->appendChild(embedPanel.release(), ec, AttachLazily);
808    if (ec)
809        return 0;
810
811    return controls.release();
812}
813
814void MediaControlsBlackBerry::setMediaController(MediaControllerInterface* controller)
815{
816    if (m_mediaController == controller)
817        return;
818
819    MediaControls::setMediaController(controller);
820
821    if (m_buttonContainer)
822        m_buttonContainer->setMediaController(controller);
823    if (m_timeDisplayContainer)
824        m_timeDisplayContainer->setMediaController(controller);
825    if (m_fullscreenTimeDisplayContainer)
826        m_fullscreenTimeDisplayContainer->setMediaController(controller);
827    if (m_fullscreenPlayButton)
828        m_fullscreenPlayButton->setMediaController(controller);
829    if (m_fullscreenCurrentTimeDisplay)
830        m_fullscreenCurrentTimeDisplay->setMediaController(controller);
831    if (m_fullscreenTimeline)
832        m_fullscreenTimeline->setMediaController(controller);
833    if (m_timeRemainingDisplay)
834        m_timeRemainingDisplay->setMediaController(controller);
835    if (m_fullscreenTimeRemainingDisplay)
836        m_fullscreenTimeRemainingDisplay->setMediaController(controller);
837    if (m_timelineContainer)
838        m_timelineContainer->setMediaController(controller);
839    if (m_fullscreenTimelineContainer)
840        m_fullscreenTimelineContainer->setMediaController(controller);
841    if (m_fullscreenFullScreenButton)
842        m_fullscreenFullScreenButton->setMediaController(controller);
843    if (m_muteButton)
844        m_muteButton->setMediaController(controller);
845    if (m_volumeSliderContainer)
846        m_volumeSliderContainer->setMediaController(controller);
847    if (m_embeddedPanel)
848        m_embeddedPanel->setMediaController(controller);
849    reset();
850}
851
852void MediaControlsBlackBerry::show()
853{
854    if (m_isFullscreen) {
855        m_panel->setIsDisplayed(true);
856        m_panel->show();
857    } else {
858        m_embeddedPanel->setIsDisplayed(true);
859        m_embeddedPanel->show();
860    }
861}
862
863void MediaControlsBlackBerry::hide()
864{
865    if (m_isFullscreen) {
866        m_panel->setIsDisplayed(false);
867        m_panel->hide();
868    } else {
869        m_embeddedPanel->setIsDisplayed(false);
870        m_embeddedPanel->hide();
871        m_volumeSliderContainer->hide();
872    }
873}
874
875void MediaControlsBlackBerry::makeOpaque()
876{
877    if (m_isFullscreen)
878        m_panel->makeOpaque();
879    else
880        m_embeddedPanel->makeOpaque();
881}
882
883void MediaControlsBlackBerry::makeTransparent()
884{
885    if (m_isFullscreen)
886        m_panel->makeTransparent();
887    else {
888        m_embeddedPanel->makeTransparent();
889        m_volumeSliderContainer->hide();
890    }
891}
892
893void MediaControlsBlackBerry::reset()
894{
895    Page* page = document()->page();
896    if (!page)
897        return;
898
899    updateStatusDisplay();
900
901    if (m_fullScreenButton) {
902        if (m_mediaController->supportsFullscreen())
903            m_fullScreenButton->show();
904        else
905            m_fullScreenButton->hide();
906    }
907    if (m_fullscreenFullScreenButton) {
908        if (m_mediaController->supportsFullscreen())
909            m_fullscreenFullScreenButton->show();
910        else
911            m_fullscreenFullScreenButton->hide();
912    }
913    double duration = m_mediaController->duration();
914    if (std::isfinite(duration) || page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
915        double now = m_mediaController->currentTime();
916        m_timeline->setDuration(duration);
917        m_fullscreenTimeline->setDuration(duration);
918        m_timelineContainer->show();
919        m_fullscreenTimelineContainer->show();
920        m_timeline->setPosition(now);
921        m_fullscreenTimeline->setPosition(now);
922        updateCurrentTimeDisplay();
923    } else {
924        m_timelineContainer->hide();
925        m_fullscreenTimelineContainer->hide();
926    }
927
928    if (m_mediaController->hasAudio() || page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
929        m_muteButton->show();
930    else
931        m_muteButton->hide();
932
933    if (m_volumeSlider)
934        m_volumeSlider->setVolume(m_mediaController->volume());
935
936    if (m_toggleClosedCaptionsButton) {
937        if (m_mediaController->hasClosedCaptions())
938            m_toggleClosedCaptionsButton->show();
939        else
940            m_toggleClosedCaptionsButton->hide();
941    }
942
943    if (m_playButton)
944        m_playButton->updateDisplayType();
945
946    if (m_fullscreenPlayButton)
947        m_fullscreenPlayButton->updateDisplayType();
948
949    makeOpaque();
950}
951
952void MediaControlsBlackBerry::bufferingProgressed()
953{
954    // We only need to update buffering progress when paused, during normal
955    // playback playbackProgressed() will take care of it.
956    if (m_mediaController->paused()) {
957        double now = m_mediaController->currentTime();
958        m_timeline->setPosition(now);
959        m_fullscreenTimeline->setPosition(now);
960    }
961}
962
963void MediaControlsBlackBerry::playbackStarted()
964{
965    double now = m_mediaController->currentTime();
966    m_playButton->updateDisplayType();
967    m_fullscreenPlayButton->updateDisplayType();
968    m_timeline->setPosition(now);
969    m_fullscreenTimeline->setPosition(now);
970    updateCurrentTimeDisplay();
971
972    if (m_isFullscreen)
973        startHideFullscreenControlsTimer();
974}
975
976void MediaControlsBlackBerry::playbackProgressed()
977{
978    double now = m_mediaController->currentTime();
979    m_timeline->setPosition(now);
980    m_fullscreenTimeline->setPosition(now);
981    updateCurrentTimeDisplay();
982
983    if (!m_isMouseOverControls && m_mediaController->hasVideo())
984        makeTransparent();
985}
986
987void MediaControlsBlackBerry::playbackStopped()
988{
989    double now = m_mediaController->currentTime();
990    m_playButton->updateDisplayType();
991    m_fullscreenPlayButton->updateDisplayType();
992    m_timeline->setPosition(now);
993    m_fullscreenTimeline->setPosition(now);
994    updateCurrentTimeDisplay();
995    makeOpaque();
996
997    stopHideFullscreenControlsTimer();
998}
999
1000void MediaControlsBlackBerry::updateCurrentTimeDisplay()
1001{
1002    double now = m_mediaController->currentTime();
1003    double duration = m_mediaController->duration();
1004
1005    Page* page = document()->page();
1006    if (!page)
1007        return;
1008
1009    // Allow the theme to format the time.
1010    m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), IGNORE_EXCEPTION);
1011    m_currentTimeDisplay->setCurrentValue(now);
1012    m_fullscreenCurrentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), IGNORE_EXCEPTION);
1013    m_fullscreenCurrentTimeDisplay->setCurrentValue(now);
1014    m_timeRemainingDisplay->setInnerText(page->theme()->formatMediaControlsRemainingTime(now, duration), IGNORE_EXCEPTION);
1015    m_timeRemainingDisplay->setCurrentValue(now - duration);
1016    m_fullscreenTimeRemainingDisplay->setInnerText(page->theme()->formatMediaControlsRemainingTime(now, duration), IGNORE_EXCEPTION);
1017    m_fullscreenTimeRemainingDisplay->setCurrentValue(now - duration);
1018}
1019
1020void MediaControlsBlackBerry::reportedError()
1021{
1022    Page* page = document()->page();
1023    if (!page)
1024        return;
1025
1026    if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
1027        m_timelineContainer->hide();
1028        m_fullscreenTimelineContainer->hide();
1029    }
1030
1031    if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
1032        m_muteButton->hide();
1033
1034    if (m_fullScreenButton)
1035        m_fullScreenButton->hide();
1036
1037    if (m_fullScreenDivider)
1038        m_fullScreenDivider->hide();
1039
1040    if (m_fullscreenFullScreenButton)
1041        m_fullscreenFullScreenButton->hide();
1042
1043    if (m_volumeSliderContainer)
1044        m_volumeSliderContainer->hide();
1045
1046    if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
1047        m_toggleClosedCaptionsButton->hide();
1048}
1049
1050void MediaControlsBlackBerry::changedMute()
1051{
1052    m_muteButton->changedMute();
1053}
1054
1055void MediaControlsBlackBerry::enteredFullscreen()
1056{
1057    MediaControls::enteredFullscreen();
1058
1059    m_panel->setCanBeDragged(true);
1060    m_embeddedPanel->setCanBeDragged(true);
1061
1062    if (m_fullscreenFullScreenButton)
1063        m_fullscreenFullScreenButton->setIsFullscreen(true);
1064}
1065
1066void MediaControlsBlackBerry::exitedFullscreen()
1067{
1068    m_panel->setCanBeDragged(false);
1069    m_embeddedPanel->setCanBeDragged(false);
1070
1071    if (m_fullscreenFullScreenButton)
1072        m_fullscreenFullScreenButton->setIsFullscreen(false);
1073
1074    // We will keep using the panel, but we want it to go back to the standard position.
1075    // This will matter right away because we use the panel even when not fullscreen.
1076    // And if we reenter fullscreen we also want the panel in the standard position.
1077    m_panel->resetPosition();
1078    m_embeddedPanel->resetPosition();
1079
1080    MediaControls::exitedFullscreen();
1081}
1082
1083void MediaControlsBlackBerry::showVolumeSlider()
1084{
1085    if (!m_mediaController->hasAudio())
1086        return;
1087
1088    if (m_volumeSliderContainer)
1089        m_volumeSliderContainer->show();
1090}
1091
1092void MediaControlsBlackBerry::toggleVolumeSlider()
1093{
1094    if (!m_mediaController->hasAudio())
1095        return;
1096
1097    if (m_volumeSliderContainer) {
1098        if (m_volumeSliderContainer->renderer() && m_volumeSliderContainer->renderer()->visibleToHitTesting())
1099            m_volumeSliderContainer->hide();
1100        else
1101            m_volumeSliderContainer->show();
1102    }
1103}
1104
1105bool MediaControlsBlackBerry::shouldHideControls()
1106{
1107    if (m_isFullscreen)
1108        return !m_panel->hovered();
1109    return !m_embeddedPanel->hovered();
1110}
1111}
1112
1113#endif
1114