[GTK] add support for subtitles on webkit2GTK
[WebKit-https.git] / Source / WebCore / html / shadow / MediaControlElements.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #if ENABLE(VIDEO)
33 #include "MediaControlElements.h"
34
35 #include "CaptionUserPreferences.h"
36 #include "DOMTokenList.h"
37 #include "EventHandler.h"
38 #include "EventNames.h"
39 #include "ExceptionCodePlaceholder.h"
40 #include "Frame.h"
41 #include "GraphicsContext.h"
42 #include "HTMLVideoElement.h"
43 #include "ImageBuffer.h"
44 #include "Language.h"
45 #include "LocalizedStrings.h"
46 #include "MediaControls.h"
47 #include "MouseEvent.h"
48 #include "Page.h"
49 #include "PageGroup.h"
50 #include "RenderLayer.h"
51 #include "RenderMediaControlElements.h"
52 #include "RenderSlider.h"
53 #include "RenderTheme.h"
54 #include "RenderVideo.h"
55 #include "RenderView.h"
56 #include "Settings.h"
57 #include "ShadowRoot.h"
58 #if ENABLE(VIDEO_TRACK)
59 #include "TextTrack.h"
60 #include "TextTrackList.h"
61 #endif
62
63 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
64 #include "RenderPart.h"
65 #endif
66
67 namespace WebCore {
68
69 using namespace HTMLNames;
70
71 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
72 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
73
74 MediaControlPanelElement::MediaControlPanelElement(Document* document)
75     : MediaControlDivElement(document, MediaControlsPanel)
76     , m_canBeDragged(false)
77     , m_isBeingDragged(false)
78     , m_isDisplayed(false)
79     , m_opaque(true)
80     , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
81 {
82 }
83
84 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document)
85 {
86     return adoptRef(new MediaControlPanelElement(document));
87 }
88
89 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
90 {
91     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral));
92     return id;
93 }
94
95 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
96 {
97     if (!m_canBeDragged)
98         return;
99
100     if (m_isBeingDragged)
101         return;
102
103     RenderObject* renderer = this->renderer();
104     if (!renderer || !renderer->isBox())
105         return;
106
107     Frame* frame = document().frame();
108     if (!frame)
109         return;
110
111     m_lastDragEventLocation = eventLocation;
112
113     frame->eventHandler().setCapturingMouseEventsNode(this);
114
115     m_isBeingDragged = true;
116 }
117
118 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
119 {
120     if (!m_isBeingDragged)
121         return;
122
123     LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation;
124     m_cumulativeDragOffset.move(distanceDragged);
125     m_lastDragEventLocation = eventLocation;
126     setPosition(m_cumulativeDragOffset);
127 }
128
129 void MediaControlPanelElement::endDrag()
130 {
131     if (!m_isBeingDragged)
132         return;
133
134     m_isBeingDragged = false;
135
136     Frame* frame = document().frame();
137     if (!frame)
138         return;
139
140     frame->eventHandler().setCapturingMouseEventsNode(0);
141 }
142
143 void MediaControlPanelElement::startTimer()
144 {
145     stopTimer();
146
147     // The timer is required to set the property display:'none' on the panel,
148     // such that captions are correctly displayed at the bottom of the video
149     // at the end of the fadeout transition.
150     double duration = document().page() ? document().page()->theme()->mediaControlsFadeOutDuration() : 0;
151     m_transitionTimer.startOneShot(duration);
152 }
153
154 void MediaControlPanelElement::stopTimer()
155 {
156     if (m_transitionTimer.isActive())
157         m_transitionTimer.stop();
158 }
159
160 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
161 {
162     if (!m_opaque)
163         hide();
164
165     stopTimer();
166 }
167
168 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
169 {
170     double left = position.x();
171     double top = position.y();
172
173     // Set the left and top to control the panel's position; this depends on it being absolute positioned.
174     // Set the margin to zero since the position passed in will already include the effect of the margin.
175     setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
176     setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
177     setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
178     setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
179
180     classList()->add("dragged", IGNORE_EXCEPTION);
181 }
182
183 void MediaControlPanelElement::resetPosition()
184 {
185     removeInlineStyleProperty(CSSPropertyLeft);
186     removeInlineStyleProperty(CSSPropertyTop);
187     removeInlineStyleProperty(CSSPropertyMarginLeft);
188     removeInlineStyleProperty(CSSPropertyMarginTop);
189
190     classList()->remove("dragged", IGNORE_EXCEPTION);
191
192     m_cumulativeDragOffset.setX(0);
193     m_cumulativeDragOffset.setY(0);
194 }
195
196 void MediaControlPanelElement::makeOpaque()
197 {
198     if (m_opaque)
199         return;
200
201     double duration = document().page() ? document().page()->theme()->mediaControlsFadeInDuration() : 0;
202
203     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
204     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
205     setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
206
207     m_opaque = true;
208
209     if (m_isDisplayed)
210         show();
211 }
212
213 void MediaControlPanelElement::makeTransparent()
214 {
215     if (!m_opaque)
216         return;
217
218     double duration = document().page() ? document().page()->theme()->mediaControlsFadeOutDuration() : 0;
219
220     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
221     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
222     setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
223
224     m_opaque = false;
225     startTimer();
226 }
227
228 void MediaControlPanelElement::defaultEventHandler(Event* event)
229 {
230     MediaControlDivElement::defaultEventHandler(event);
231
232     if (event->isMouseEvent()) {
233         LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
234         if (event->type() == eventNames().mousedownEvent && event->target() == this) {
235             startDrag(location);
236             event->setDefaultHandled();
237         } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
238             continueDrag(location);
239         else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
240             continueDrag(location);
241             endDrag();
242             event->setDefaultHandled();
243         }
244     }
245 }
246
247 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
248 {
249     if (m_canBeDragged == canBeDragged)
250         return;
251
252     m_canBeDragged = canBeDragged;
253
254     if (!canBeDragged)
255         endDrag();
256 }
257
258 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
259 {
260     m_isDisplayed = isDisplayed;
261 }
262
263 // ----------------------------
264
265 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(Document* document)
266     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
267     : MediaControlDivElement(document, MediaControlsPanel)
268 {
269 }
270
271 PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document* document)
272 {
273     return adoptRef(new MediaControlPanelEnclosureElement(document));
274 }
275
276 const AtomicString& MediaControlPanelEnclosureElement::shadowPseudoId() const
277 {
278     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
279     return id;
280 }
281
282 // ----------------------------
283
284 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document* document)
285     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
286     : MediaControlDivElement(document, MediaControlsPanel)
287 {
288 }
289
290 PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document* document)
291 {
292     return adoptRef(new MediaControlOverlayEnclosureElement(document));
293 }
294
295 const AtomicString& MediaControlOverlayEnclosureElement::shadowPseudoId() const
296 {
297     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
298     return id;
299 }
300
301 // ----------------------------
302
303 MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document)
304     : MediaControlDivElement(document, MediaTimelineContainer)
305 {
306 }
307
308 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document* document)
309 {
310     RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document));
311     element->hide();
312     return element.release();
313 }
314
315 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
316 {
317     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container", AtomicString::ConstructFromLiteral));
318     return id;
319 }
320
321 void MediaControlTimelineContainerElement::setTimeDisplaysHidden(bool hidden)
322 {
323     for (unsigned i = 0; i < childNodeCount(); ++i) {
324         Node* child = childNode(i);
325         if (!child || !child->isElementNode())
326             continue;
327         Element* element = static_cast<Element*>(child);
328         if (element->shadowPseudoId() != getMediaControlTimeRemainingDisplayElementShadowPseudoId()
329             && element->shadowPseudoId() != getMediaControlCurrentTimeDisplayElementShadowPseudoId())
330             continue;
331
332         MediaControlTimeDisplayElement* timeDisplay = static_cast<MediaControlTimeDisplayElement*>(element);
333         if (hidden)
334             timeDisplay->hide();
335         else
336             timeDisplay->show();
337     }
338 }
339
340 RenderObject* MediaControlTimelineContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
341 {
342     return new (arena) RenderMediaControlTimelineContainer(this);
343 }
344
345 // ----------------------------
346
347 MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* document)
348     : MediaControlDivElement(document, MediaVolumeSliderContainer)
349 {
350 }
351
352 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document* document)
353 {
354     RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document));
355     element->hide();
356     return element.release();
357 }
358
359 RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
360 {
361     return new (arena) RenderMediaVolumeSliderContainer(this);
362 }
363
364 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
365 {
366     if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
367         return;
368
369     // Poor man's mouseleave event detection.
370     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
371     if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
372         return;
373
374     if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
375         return;
376
377     hide();
378 }
379
380 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
381 {
382     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container", AtomicString::ConstructFromLiteral));
383     return id;
384 }
385
386 // ----------------------------
387
388 MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document)
389     : MediaControlDivElement(document, MediaStatusDisplay)
390     , m_stateBeingDisplayed(Nothing)
391 {
392 }
393
394 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document* document)
395 {
396     RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(document));
397     element->hide();
398     return element.release();
399 }
400
401 void MediaControlStatusDisplayElement::update()
402 {
403     // Get the new state that we'll have to display.
404     StateBeingDisplayed newStateToDisplay = Nothing;
405
406     if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc())
407         newStateToDisplay = Loading;
408     else if (mediaController()->isLiveStream())
409         newStateToDisplay = LiveBroadcast;
410
411     if (newStateToDisplay == m_stateBeingDisplayed)
412         return;
413
414     if (m_stateBeingDisplayed == Nothing)
415         show();
416     else if (newStateToDisplay == Nothing)
417         hide();
418
419     m_stateBeingDisplayed = newStateToDisplay;
420
421     switch (m_stateBeingDisplayed) {
422     case Nothing:
423         setInnerText("", IGNORE_EXCEPTION);
424         break;
425     case Loading:
426         setInnerText(mediaElementLoadingStateText(), IGNORE_EXCEPTION);
427         break;
428     case LiveBroadcast:
429         setInnerText(mediaElementLiveBroadcastStateText(), IGNORE_EXCEPTION);
430         break;
431     }
432 }
433
434 const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
435 {
436     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display", AtomicString::ConstructFromLiteral));
437     return id;
438 }
439
440
441 // ----------------------------
442
443 MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls)
444     : MediaControlMuteButtonElement(document, MediaMuteButton)
445     , m_controls(controls)
446 {
447 }
448
449 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls)
450 {
451     ASSERT(controls);
452
453     RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
454     button->ensureUserAgentShadowRoot();
455     button->setType("button");
456     return button.release();
457 }
458
459 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
460 {
461     if (event->type() == eventNames().mouseoverEvent)
462         m_controls->showVolumeSlider();
463
464     MediaControlMuteButtonElement::defaultEventHandler(event);
465 }
466
467 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
468 {
469     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
470     return id;
471 }
472
473 // ----------------------------
474
475 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document)
476     : MediaControlMuteButtonElement(document, MediaMuteButton)
477 {
478 }
479
480 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document)
481 {
482     RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
483     button->ensureUserAgentShadowRoot();
484     button->setType("button");
485     return button.release();
486 }
487
488 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
489 {
490     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral));
491     return id;
492 }
493
494 // ----------------------------
495
496 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document)
497     : MediaControlInputElement(document, MediaPlayButton)
498 {
499 }
500
501 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document)
502 {
503     RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
504     button->ensureUserAgentShadowRoot();
505     button->setType("button");
506     return button.release();
507 }
508
509 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
510 {
511     if (event->type() == eventNames().clickEvent) {
512         if (mediaController()->canPlay())
513             mediaController()->play();
514         else
515             mediaController()->pause();
516         updateDisplayType();
517         event->setDefaultHandled();
518     }
519     HTMLInputElement::defaultEventHandler(event);
520 }
521
522 void MediaControlPlayButtonElement::updateDisplayType()
523 {
524     setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
525 }
526
527 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
528 {
529     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
530     return id;
531 }
532
533 // ----------------------------
534
535 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document)
536     : MediaControlInputElement(document, MediaOverlayPlayButton)
537 {
538 }
539
540 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document)
541 {
542     RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
543     button->ensureUserAgentShadowRoot();
544     button->setType("button");
545     return button.release();
546 }
547
548 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
549 {
550     if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
551         mediaController()->play();
552         updateDisplayType();
553         event->setDefaultHandled();
554     }
555     HTMLInputElement::defaultEventHandler(event);
556 }
557
558 void MediaControlOverlayPlayButtonElement::updateDisplayType()
559 {
560     if (mediaController()->canPlay()) {
561         show();
562     } else
563         hide();
564 }
565
566 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
567 {
568     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
569     return id;
570 }
571
572
573 // ----------------------------
574
575 MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document* document)
576     : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
577 {
578 }
579
580 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document* document)
581 {
582     RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document));
583     button->ensureUserAgentShadowRoot();
584     button->setType("button");
585     return button.release();
586 }
587
588 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
589 {
590     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button", AtomicString::ConstructFromLiteral));
591     return id;
592 }
593
594 // ----------------------------
595
596 MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document* document)
597     : MediaControlSeekButtonElement(document, MediaSeekBackButton)
598 {
599 }
600
601 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document* document)
602 {
603     RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document));
604     button->ensureUserAgentShadowRoot();
605     button->setType("button");
606     return button.release();
607 }
608
609 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
610 {
611     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button", AtomicString::ConstructFromLiteral));
612     return id;
613 }
614
615 // ----------------------------
616
617 MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document)
618     : MediaControlInputElement(document, MediaRewindButton)
619 {
620 }
621
622 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document* document)
623 {
624     RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document));
625     button->ensureUserAgentShadowRoot();
626     button->setType("button");
627     return button.release();
628 }
629
630 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
631 {
632     if (event->type() == eventNames().clickEvent) {
633         mediaController()->setCurrentTime(max(0.0, mediaController()->currentTime() - 30), IGNORE_EXCEPTION);
634         event->setDefaultHandled();
635     }
636     HTMLInputElement::defaultEventHandler(event);
637 }
638
639 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
640 {
641     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button", AtomicString::ConstructFromLiteral));
642     return id;
643 }
644
645 // ----------------------------
646
647 MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document)
648     : MediaControlInputElement(document, MediaReturnToRealtimeButton)
649 {
650 }
651
652 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document* document)
653 {
654     RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document));
655     button->ensureUserAgentShadowRoot();
656     button->setType("button");
657     button->hide();
658     return button.release();
659 }
660
661 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
662 {
663     if (event->type() == eventNames().clickEvent) {
664         mediaController()->returnToRealtime();
665         event->setDefaultHandled();
666     }
667     HTMLInputElement::defaultEventHandler(event);
668 }
669
670 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
671 {
672     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button", AtomicString::ConstructFromLiteral));
673     return id;
674 }
675
676 // ----------------------------
677
678 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document, MediaControls* controls)
679     : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
680 #if PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK)
681     , m_controls(controls)
682 #endif
683 {
684 #if !PLATFORM(MAC) && !PLATFORM(WIN) || !PLATFORM(GTK)
685     UNUSED_PARAM(controls);
686 #endif
687 }
688
689 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document, MediaControls* controls)
690 {
691     ASSERT(controls);
692
693     RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls));
694     button->ensureUserAgentShadowRoot();
695     button->setType("button");
696     button->hide();
697     return button.release();
698 }
699
700 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
701 {
702     bool captionsVisible = mediaController()->closedCaptionsVisible();
703     setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
704     setChecked(captionsVisible);
705 }
706
707 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
708 {
709     if (event->type() == eventNames().clickEvent) {
710         // FIXME: It's not great that the shared code is dictating behavior of platform-specific
711         // UI. Not all ports may want the closed captions button to toggle a list of tracks, so
712         // we have to use #if.
713         // https://bugs.webkit.org/show_bug.cgi?id=101877
714 #if !PLATFORM(MAC) && !PLATFORM(WIN) && !PLATFORM(GTK)
715         mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
716         setChecked(mediaController()->closedCaptionsVisible());
717         updateDisplayType();
718 #else
719         m_controls->toggleClosedCaptionTrackList();
720 #endif
721         event->setDefaultHandled();
722     }
723
724     HTMLInputElement::defaultEventHandler(event);
725 }
726
727 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
728 {
729     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
730     return id;
731 }
732
733 // ----------------------------
734
735 MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement(Document* document)
736     : MediaControlDivElement(document, MediaClosedCaptionsContainer)
737 {
738 }
739
740 PassRefPtr<MediaControlClosedCaptionsContainerElement> MediaControlClosedCaptionsContainerElement::create(Document* document)
741 {
742     RefPtr<MediaControlClosedCaptionsContainerElement> element = adoptRef(new MediaControlClosedCaptionsContainerElement(document));
743     element->setAttribute(dirAttr, "auto");
744     element->hide();
745     return element.release();
746 }
747
748 const AtomicString& MediaControlClosedCaptionsContainerElement::shadowPseudoId() const
749 {
750     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-container", AtomicString::ConstructFromLiteral));
751     return id;
752 }
753
754 // ----------------------------
755
756 MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document* document, MediaControls* controls)
757     : MediaControlDivElement(document, MediaClosedCaptionsTrackList)
758     , m_controls(controls)
759 {
760 }
761
762 PassRefPtr<MediaControlClosedCaptionsTrackListElement> MediaControlClosedCaptionsTrackListElement::create(Document* document, MediaControls* controls)
763 {
764     ASSERT(controls);
765     RefPtr<MediaControlClosedCaptionsTrackListElement> element = adoptRef(new MediaControlClosedCaptionsTrackListElement(document, controls));
766     return element.release();
767 }
768
769 void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* event)
770 {
771 #if ENABLE(VIDEO_TRACK)
772     if (event->type() == eventNames().clickEvent) {
773         Node* target = event->target()->toNode();
774         if (!target || !target->isElementNode())
775             return;
776
777         // When we created the elements in the track list, we gave them a custom
778         // attribute representing the index in the HTMLMediaElement's list of tracks.
779         // Check if the event target has such a custom element and, if so,
780         // tell the HTMLMediaElement to enable that track.
781
782         RefPtr<TextTrack> textTrack;
783         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(toElement(target));
784         if (iter != m_menuToTrackMap.end())
785             textTrack = iter->value;
786         m_menuToTrackMap.clear();
787         m_controls->toggleClosedCaptionTrackList();
788         if (!textTrack)
789             return;
790
791         HTMLMediaElement* mediaElement = toParentMediaElement(this);
792         if (!mediaElement)
793             return;
794
795         mediaElement->setSelectedTextTrack(textTrack.get());
796
797         updateDisplay();
798     }
799
800     MediaControlDivElement::defaultEventHandler(event);
801 #endif
802 }
803
804 const AtomicString& MediaControlClosedCaptionsTrackListElement::shadowPseudoId() const
805 {
806     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-track-list", AtomicString::ConstructFromLiteral));
807     return id;
808 }
809
810 void MediaControlClosedCaptionsTrackListElement::updateDisplay()
811 {
812 #if ENABLE(VIDEO_TRACK)
813     DEFINE_STATIC_LOCAL(AtomicString, selectedClassValue, ("selected", AtomicString::ConstructFromLiteral));
814
815     if (!mediaController()->hasClosedCaptions())
816         return;
817
818     if (!document().page())
819         return;
820     CaptionUserPreferences::CaptionDisplayMode displayMode = document().page()->group().captionPreferences()->captionDisplayMode();
821
822     HTMLMediaElement* mediaElement = toParentMediaElement(this);
823     if (!mediaElement)
824         return;
825
826     TextTrackList* trackList = mediaElement->textTracks();
827     if (!trackList || !trackList->length())
828         return;
829
830     rebuildTrackListMenu();
831
832     RefPtr<Element> offMenuItem;
833     bool trackMenuItemSelected = false;
834
835     for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) {
836         RefPtr<Element> trackItem = m_menuItems[i];
837
838         RefPtr<TextTrack> textTrack;
839         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(trackItem.get());
840         if (iter == m_menuToTrackMap.end())
841             continue;
842         textTrack = iter->value;
843         if (!textTrack)
844             continue;
845
846         if (textTrack == TextTrack::captionMenuOffItem()) {
847             offMenuItem = trackItem;
848             continue;
849         }
850
851         if (textTrack == TextTrack::captionMenuAutomaticItem()) {
852             if (displayMode == CaptionUserPreferences::Automatic)
853                 trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
854             else
855                 trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
856             continue;
857         }
858
859         if (displayMode != CaptionUserPreferences::Automatic && textTrack->mode() == TextTrack::showingKeyword()) {
860             trackMenuItemSelected = true;
861             trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
862         } else
863             trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
864     }
865
866     if (offMenuItem) {
867         if (displayMode == CaptionUserPreferences::ForcedOnly && !trackMenuItemSelected)
868             offMenuItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
869         else
870             offMenuItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
871     }
872 #endif
873 }
874
875 void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
876 {
877 #if ENABLE(VIDEO_TRACK)
878     // Remove any existing content.
879     removeChildren();
880     m_menuItems.clear();
881     m_menuToTrackMap.clear();
882
883     if (!mediaController()->hasClosedCaptions())
884         return;
885
886     HTMLMediaElement* mediaElement = toParentMediaElement(this);
887     if (!mediaElement)
888         return;
889
890     TextTrackList* trackList = mediaElement->textTracks();
891     if (!trackList || !trackList->length())
892         return;
893
894     if (!document().page())
895         return;
896     CaptionUserPreferences* captionPreferences = document().page()->group().captionPreferences();
897     Vector<RefPtr<TextTrack> > tracksForMenu = captionPreferences->sortedTrackListForMenu(trackList);
898
899     RefPtr<Element> captionsHeader = document().createElement(h3Tag, ASSERT_NO_EXCEPTION);
900     captionsHeader->appendChild(document().createTextNode(textTrackSubtitlesText()));
901     appendChild(captionsHeader);
902     RefPtr<Element> captionsMenuList = document().createElement(ulTag, ASSERT_NO_EXCEPTION);
903
904     for (unsigned i = 0, length = tracksForMenu.size(); i < length; ++i) {
905         RefPtr<TextTrack> textTrack = tracksForMenu[i];
906         RefPtr<Element> menuItem = document().createElement(liTag, ASSERT_NO_EXCEPTION);
907         menuItem->appendChild(document().createTextNode(captionPreferences->displayNameForTrack(textTrack.get())));
908         captionsMenuList->appendChild(menuItem);
909         m_menuItems.append(menuItem);
910         m_menuToTrackMap.add(menuItem, textTrack);
911     }
912
913     appendChild(captionsMenuList);
914 #endif
915 }
916
917 // ----------------------------
918
919 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls)
920     : MediaControlInputElement(document, MediaSlider)
921     , m_controls(controls)
922 {
923 }
924
925 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls)
926 {
927     ASSERT(controls);
928
929     RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
930     timeline->ensureUserAgentShadowRoot();
931     timeline->setType("range");
932     timeline->setAttribute(precisionAttr, "float");
933     return timeline.release();
934 }
935
936 void MediaControlTimelineElement::defaultEventHandler(Event* event)
937 {
938     // Left button is 0. Rejects mouse events not from left button.
939     if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
940         return;
941
942     if (!attached())
943         return;
944
945     if (event->type() == eventNames().mousedownEvent)
946         mediaController()->beginScrubbing();
947
948     if (event->type() == eventNames().mouseupEvent)
949         mediaController()->endScrubbing();
950
951     MediaControlInputElement::defaultEventHandler(event);
952
953     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
954         return;
955
956     double time = value().toDouble();
957     if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime())
958         mediaController()->setCurrentTime(time, IGNORE_EXCEPTION);
959
960     RenderSlider* slider = toRenderSlider(renderer());
961     if (slider && slider->inDragMode())
962         m_controls->updateCurrentTimeDisplay();
963 }
964
965 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
966 {
967     if (!attached())
968         return false;
969
970     return true;
971 }
972
973 void MediaControlTimelineElement::setPosition(double currentTime)
974 {
975     setValue(String::number(currentTime));
976 }
977
978 void MediaControlTimelineElement::setDuration(double duration)
979 {
980     setAttribute(maxAttr, String::number(std::isfinite(duration) ? duration : 0));
981 }
982
983
984 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
985 {
986     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral));
987     return id;
988 }
989
990 // ----------------------------
991
992 MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document* document)
993     : MediaControlVolumeSliderElement(document)
994 {
995 }
996
997 PassRefPtr<MediaControlPanelVolumeSliderElement> MediaControlPanelVolumeSliderElement::create(Document* document)
998 {
999     RefPtr<MediaControlPanelVolumeSliderElement> slider = adoptRef(new MediaControlPanelVolumeSliderElement(document));
1000     slider->ensureUserAgentShadowRoot();
1001     slider->setType("range");
1002     slider->setAttribute(precisionAttr, "float");
1003     slider->setAttribute(maxAttr, "1");
1004     return slider.release();
1005 }
1006
1007 const AtomicString& MediaControlPanelVolumeSliderElement::shadowPseudoId() const
1008 {
1009     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral));
1010     return id;
1011 }
1012
1013 // ----------------------------
1014
1015 MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document* document)
1016     : MediaControlVolumeSliderElement(document)
1017 {
1018 }
1019
1020 PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document* document)
1021 {
1022     RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(document));
1023     slider->ensureUserAgentShadowRoot();
1024     slider->setType("range");
1025     slider->setAttribute(precisionAttr, "float");
1026     slider->setAttribute(maxAttr, "1");
1027     return slider.release();
1028 }
1029
1030 const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
1031 {
1032     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider", AtomicString::ConstructFromLiteral));
1033     return id;
1034 }
1035
1036 // ----------------------------
1037
1038 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document)
1039     : MediaControlInputElement(document, MediaEnterFullscreenButton)
1040 {
1041 }
1042
1043 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document)
1044 {
1045     RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document));
1046     button->ensureUserAgentShadowRoot();
1047     button->setType("button");
1048     button->hide();
1049     return button.release();
1050 }
1051
1052 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
1053 {
1054     if (event->type() == eventNames().clickEvent) {
1055 #if ENABLE(FULLSCREEN_API)
1056         // Only use the new full screen API if the fullScreenEnabled setting has
1057         // been explicitly enabled. Otherwise, use the old fullscreen API. This
1058         // allows apps which embed a WebView to retain the existing full screen
1059         // video implementation without requiring them to implement their own full
1060         // screen behavior.
1061         if (document().settings() && document().settings()->fullScreenEnabled()) {
1062             if (document().webkitIsFullScreen() && document().webkitCurrentFullScreenElement() == toParentMediaElement(this))
1063                 document().webkitCancelFullScreen();
1064             else
1065                 document().requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
1066         } else
1067 #endif
1068             mediaController()->enterFullscreen();
1069         event->setDefaultHandled();
1070     }
1071     HTMLInputElement::defaultEventHandler(event);
1072 }
1073
1074 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
1075 {
1076     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral));
1077     return id;
1078 }
1079
1080 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
1081 {
1082     setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
1083 }
1084
1085 // ----------------------------
1086
1087 MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document* document)
1088     : MediaControlInputElement(document, MediaUnMuteButton)
1089 {
1090 }
1091
1092 PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document* document)
1093 {
1094     RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(document));
1095     button->ensureUserAgentShadowRoot();
1096     button->setType("button");
1097     return button.release();
1098 }
1099
1100 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
1101 {
1102     if (event->type() == eventNames().clickEvent) {
1103         ExceptionCode code = 0;
1104         mediaController()->setVolume(0, code);
1105         event->setDefaultHandled();
1106     }
1107     HTMLInputElement::defaultEventHandler(event);
1108 }
1109
1110 const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
1111 {
1112     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button", AtomicString::ConstructFromLiteral));
1113     return id;
1114 }
1115
1116 // ----------------------------
1117
1118 MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document* document)
1119 : MediaControlInputElement(document, MediaMuteButton)
1120 {
1121 }
1122
1123 PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document* document)
1124 {
1125     RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(document));
1126     button->ensureUserAgentShadowRoot();
1127     button->setType("button");
1128     return button.release();
1129 }
1130
1131 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
1132 {
1133     if (event->type() == eventNames().clickEvent) {
1134         ExceptionCode code = 0;
1135         mediaController()->setVolume(1, code);
1136         event->setDefaultHandled();
1137     }
1138     HTMLInputElement::defaultEventHandler(event);
1139 }
1140
1141 const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
1142 {
1143     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button", AtomicString::ConstructFromLiteral));
1144     return id;
1145 }
1146
1147 // ----------------------------
1148
1149 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document)
1150     : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay)
1151 {
1152 }
1153
1154 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document)
1155 {
1156     return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
1157 }
1158
1159 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId()
1160 {
1161     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral));
1162     return id;
1163 }
1164
1165 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
1166 {
1167     return getMediaControlTimeRemainingDisplayElementShadowPseudoId();
1168 }
1169
1170 // ----------------------------
1171
1172 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document)
1173     : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay)
1174 {
1175 }
1176
1177 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document)
1178 {
1179     return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
1180 }
1181
1182 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId()
1183 {
1184     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral));
1185     return id;
1186 }
1187
1188 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
1189 {
1190     return getMediaControlCurrentTimeDisplayElementShadowPseudoId();
1191 }
1192
1193 // ----------------------------
1194
1195 #if ENABLE(VIDEO_TRACK)
1196
1197 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document)
1198     : MediaControlDivElement(document, MediaTextTrackDisplayContainer)
1199     , m_updateTimer(this, &MediaControlTextTrackContainerElement::updateTimerFired)
1200     , m_fontSize(0)
1201     , m_fontSizeIsImportant(false)
1202 {
1203 }
1204
1205 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document)
1206 {
1207     RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
1208     element->hide();
1209     return element.release();
1210 }
1211
1212 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
1213 {
1214     return new (arena) RenderTextTrackContainerElement(this);
1215 }
1216
1217 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId()
1218 {
1219     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral));
1220     return id;
1221 }
1222     
1223 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
1224 {
1225     return textTrackContainerElementShadowPseudoId();
1226 }
1227
1228 void MediaControlTextTrackContainerElement::updateDisplay()
1229 {
1230     if (!mediaController()->closedCaptionsVisible())
1231         removeChildren();
1232
1233     HTMLMediaElement* mediaElement = toParentMediaElement(this);
1234     // 1. If the media element is an audio element, or is another playback
1235     // mechanism with no rendering area, abort these steps. There is nothing to
1236     // render.
1237     if (!mediaElement || !mediaElement->isVideo())
1238         return;
1239
1240     // 2. Let video be the media element or other playback mechanism.
1241     HTMLVideoElement* video = toHTMLVideoElement(mediaElement);
1242
1243     // 3. Let output be an empty list of absolutely positioned CSS block boxes.
1244     Vector<RefPtr<HTMLDivElement> > output;
1245
1246     // 4. If the user agent is exposing a user interface for video, add to
1247     // output one or more completely transparent positioned CSS block boxes that
1248     // cover the same region as the user interface.
1249
1250     // 5. If the last time these rules were run, the user agent was not exposing
1251     // a user interface for video, but now it is, let reset be true. Otherwise,
1252     // let reset be false.
1253
1254     // There is nothing to be done explicitly for 4th and 5th steps, as
1255     // everything is handled through CSS. The caption box is on top of the
1256     // controls box, in a container set with the -webkit-box display property.
1257
1258     // 6. Let tracks be the subset of video's list of text tracks that have as
1259     // their rules for updating the text track rendering these rules for
1260     // updating the display of WebVTT text tracks, and whose text track mode is
1261     // showing or showing by default.
1262     // 7. Let cues be an empty list of text track cues.
1263     // 8. For each track track in tracks, append to cues all the cues from
1264     // track's list of cues that have their text track cue active flag set.
1265     CueList activeCues = video->currentlyActiveCues();
1266
1267     // 9. If reset is false, then, for each text track cue cue in cues: if cue's
1268     // text track cue display state has a set of CSS boxes, then add those boxes
1269     // to output, and remove cue from cues.
1270
1271     // There is nothing explicitly to be done here, as all the caching occurs
1272     // within the TextTrackCue instance itself. If parameters of the cue change,
1273     // the display tree is cleared.
1274
1275     // 10. For each text track cue cue in cues that has not yet had
1276     // corresponding CSS boxes added to output, in text track cue order, run the
1277     // following substeps:
1278     for (size_t i = 0; i < activeCues.size(); ++i) {
1279         TextTrackCue* cue = activeCues[i].data();
1280
1281         ASSERT(cue->isActive());
1282         if (!cue->track() || !cue->track()->isRendered() || !cue->isActive() || cue->text().isEmpty())
1283             continue;
1284
1285         RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size());
1286         if (displayBox->hasChildNodes() && !contains(displayBox.get())) {
1287             // Note: the display tree of a cue is removed when the active flag of the cue is unset.
1288             appendChild(displayBox, ASSERT_NO_EXCEPTION, AttachNow);
1289             cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
1290         }
1291     }
1292
1293     // 11. Return output.
1294     if (hasChildNodes()) {
1295         show();
1296         if (mediaElement->requiresTextTrackRepresentation()) {
1297             if (!m_textTrackRepresentation)
1298                 m_textTrackRepresentation = TextTrackRepresentation::create(this);
1299             mediaElement->setTextTrackRepresentation(m_textTrackRepresentation.get());
1300
1301             if (Page* page = document().page())
1302                 m_textTrackRepresentation->setContentScale(page->deviceScaleFactor());
1303
1304             m_textTrackRepresentation->update();
1305             setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
1306             setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
1307         }
1308     } else {
1309         hide();
1310         clearTextTrackRepresentation();
1311     }
1312 }
1313
1314 void MediaControlTextTrackContainerElement::updateTimerFired(Timer<MediaControlTextTrackContainerElement>*)
1315 {
1316     if (!document().page())
1317         return;
1318
1319     if (m_textTrackRepresentation) {
1320         setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
1321         setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
1322     }
1323     
1324     HTMLMediaElement* mediaElement = toParentMediaElement(this);
1325     if (!mediaElement)
1326         return;
1327
1328     float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
1329     float fontScale = document().page()->group().captionPreferences()->captionFontSizeScaleAndImportance(m_fontSizeIsImportant);
1330     m_fontSize = lroundf(smallestDimension * fontScale);
1331     
1332     CueList activeCues = mediaElement->currentlyActiveCues();
1333     for (size_t i = 0; i < activeCues.size(); ++i) {
1334         TextTrackCue* cue = activeCues[i].data();
1335         cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
1336     }
1337     updateDisplay();
1338 }
1339
1340 void MediaControlTextTrackContainerElement::clearTextTrackRepresentation()
1341 {
1342     if (HTMLMediaElement* mediaElement = toParentMediaElement(this))
1343         mediaElement->setTextTrackRepresentation(0);
1344     m_textTrackRepresentation = nullptr;
1345     removeInlineStyleProperty(CSSPropertyPosition);
1346     removeInlineStyleProperty(CSSPropertyWidth);
1347     removeInlineStyleProperty(CSSPropertyHeight);
1348     removeInlineStyleProperty(CSSPropertyLeft);
1349     removeInlineStyleProperty(CSSPropertyTop);
1350 }
1351
1352 void MediaControlTextTrackContainerElement::enteredFullscreen()
1353 {
1354     updateSizes(true);
1355 }
1356
1357 void MediaControlTextTrackContainerElement::exitedFullscreen()
1358 {
1359     clearTextTrackRepresentation();
1360     updateSizes(true);
1361 }
1362
1363 void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
1364 {
1365     HTMLMediaElement* mediaElement = toParentMediaElement(this);
1366     if (!mediaElement)
1367         return;
1368
1369     if (!document().page())
1370         return;
1371
1372     IntRect videoBox;
1373
1374     if (m_textTrackRepresentation)
1375         videoBox = m_textTrackRepresentation->bounds();
1376     else {
1377 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1378         if (!mediaElement->renderer() || !mediaElement->renderer()->isRenderPart())
1379             return;
1380         videoBox = pixelSnappedIntRect(toRenderPart(mediaElement->renderer())->contentBoxRect());
1381 #else
1382         if (!mediaElement->renderer() || !mediaElement->renderer()->isVideo())
1383             return;
1384         videoBox = toRenderVideo(*mediaElement->renderer()).videoBox();
1385 #endif
1386     }
1387
1388     if (!forceUpdate && m_videoDisplaySize == videoBox)
1389         return;
1390     m_videoDisplaySize = videoBox;
1391
1392     m_updateTimer.startOneShot(0);
1393 }
1394
1395 PassRefPtr<Image> MediaControlTextTrackContainerElement::createTextTrackRepresentationImage()
1396 {
1397     if (!hasChildNodes())
1398         return 0;
1399
1400     Frame* frame = document().frame();
1401     if (!frame)
1402         return 0;
1403
1404     document().updateLayout();
1405
1406     RenderObject* renderer = this->renderer();
1407     if (!renderer)
1408         return 0;
1409
1410     if (!renderer->hasLayer())
1411         return 0;
1412
1413     RenderLayer* layer = toRenderLayerModelObject(renderer)->layer();
1414
1415     float deviceScaleFactor = 1;
1416     if (Page* page = document().page())
1417         deviceScaleFactor = page->deviceScaleFactor();
1418
1419     IntRect paintingRect = IntRect(IntPoint(), layer->size());
1420     OwnPtr<ImageBuffer> buffer(ImageBuffer::create(paintingRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB));
1421     if (!buffer)
1422         return 0;
1423
1424     layer->paint(buffer->context(), paintingRect, PaintBehaviorFlattenCompositingLayers, 0, 0, RenderLayer::PaintLayerPaintingCompositingAllPhases);
1425
1426     return buffer->copyImage();
1427 }
1428
1429 void MediaControlTextTrackContainerElement::textTrackRepresentationBoundsChanged(const IntRect&)
1430 {
1431     updateSizes();
1432 }
1433
1434 #endif // ENABLE(VIDEO_TRACK)
1435
1436 // ----------------------------
1437
1438 } // namespace WebCore
1439
1440 #endif // ENABLE(VIDEO)