[iOS] captions sometimes positioned incorrectly after fullscreen state change
[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 #if ENABLE(VIDEO_TRACK)
58 #include "TextTrack.h"
59 #include "TextTrackList.h"
60 #endif
61
62 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
63 #include "RenderPart.h"
64 #endif
65
66 namespace WebCore {
67
68 using namespace HTMLNames;
69
70 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
71 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
72
73 MediaControlPanelElement::MediaControlPanelElement(Document* document)
74     : MediaControlDivElement(document, MediaControlsPanel)
75     , m_canBeDragged(false)
76     , m_isBeingDragged(false)
77     , m_isDisplayed(false)
78     , m_opaque(true)
79     , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
80 {
81 }
82
83 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document)
84 {
85     return adoptRef(new MediaControlPanelElement(document));
86 }
87
88 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
89 {
90     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral));
91     return id;
92 }
93
94 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
95 {
96     if (!m_canBeDragged)
97         return;
98
99     if (m_isBeingDragged)
100         return;
101
102     RenderObject* renderer = this->renderer();
103     if (!renderer || !renderer->isBox())
104         return;
105
106     Frame* frame = document()->frame();
107     if (!frame)
108         return;
109
110     m_lastDragEventLocation = eventLocation;
111
112     frame->eventHandler()->setCapturingMouseEventsNode(this);
113
114     m_isBeingDragged = true;
115 }
116
117 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
118 {
119     if (!m_isBeingDragged)
120         return;
121
122     LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation;
123     m_cumulativeDragOffset.move(distanceDragged);
124     m_lastDragEventLocation = eventLocation;
125     setPosition(m_cumulativeDragOffset);
126 }
127
128 void MediaControlPanelElement::endDrag()
129 {
130     if (!m_isBeingDragged)
131         return;
132
133     m_isBeingDragged = false;
134
135     Frame* frame = document()->frame();
136     if (!frame)
137         return;
138
139     frame->eventHandler()->setCapturingMouseEventsNode(0);
140 }
141
142 void MediaControlPanelElement::startTimer()
143 {
144     stopTimer();
145
146     // The timer is required to set the property display:'none' on the panel,
147     // such that captions are correctly displayed at the bottom of the video
148     // at the end of the fadeout transition.
149     double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
150     m_transitionTimer.startOneShot(duration);
151 }
152
153 void MediaControlPanelElement::stopTimer()
154 {
155     if (m_transitionTimer.isActive())
156         m_transitionTimer.stop();
157 }
158
159 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
160 {
161     if (!m_opaque)
162         hide();
163
164     stopTimer();
165 }
166
167 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
168 {
169     double left = position.x();
170     double top = position.y();
171
172     // Set the left and top to control the panel's position; this depends on it being absolute positioned.
173     // Set the margin to zero since the position passed in will already include the effect of the margin.
174     setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
175     setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
176     setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
177     setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
178
179     classList()->add("dragged", IGNORE_EXCEPTION);
180 }
181
182 void MediaControlPanelElement::resetPosition()
183 {
184     removeInlineStyleProperty(CSSPropertyLeft);
185     removeInlineStyleProperty(CSSPropertyTop);
186     removeInlineStyleProperty(CSSPropertyMarginLeft);
187     removeInlineStyleProperty(CSSPropertyMarginTop);
188
189     classList()->remove("dragged", IGNORE_EXCEPTION);
190
191     m_cumulativeDragOffset.setX(0);
192     m_cumulativeDragOffset.setY(0);
193 }
194
195 void MediaControlPanelElement::makeOpaque()
196 {
197     if (m_opaque)
198         return;
199
200     double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0;
201
202     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
203     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
204     setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
205
206     m_opaque = true;
207
208     if (m_isDisplayed)
209         show();
210 }
211
212 void MediaControlPanelElement::makeTransparent()
213 {
214     if (!m_opaque)
215         return;
216
217     double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
218
219     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
220     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
221     setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
222
223     m_opaque = false;
224     startTimer();
225 }
226
227 void MediaControlPanelElement::defaultEventHandler(Event* event)
228 {
229     MediaControlDivElement::defaultEventHandler(event);
230
231     if (event->isMouseEvent()) {
232         LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
233         if (event->type() == eventNames().mousedownEvent && event->target() == this) {
234             startDrag(location);
235             event->setDefaultHandled();
236         } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
237             continueDrag(location);
238         else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
239             continueDrag(location);
240             endDrag();
241             event->setDefaultHandled();
242         }
243     }
244 }
245
246 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
247 {
248     if (m_canBeDragged == canBeDragged)
249         return;
250
251     m_canBeDragged = canBeDragged;
252
253     if (!canBeDragged)
254         endDrag();
255 }
256
257 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
258 {
259     m_isDisplayed = isDisplayed;
260 }
261
262 // ----------------------------
263
264 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(Document* document)
265     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
266     : MediaControlDivElement(document, MediaControlsPanel)
267 {
268 }
269
270 PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document* document)
271 {
272     return adoptRef(new MediaControlPanelEnclosureElement(document));
273 }
274
275 const AtomicString& MediaControlPanelEnclosureElement::shadowPseudoId() const
276 {
277     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
278     return id;
279 }
280
281 // ----------------------------
282
283 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document* document)
284     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
285     : MediaControlDivElement(document, MediaControlsPanel)
286 {
287 }
288
289 PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document* document)
290 {
291     return adoptRef(new MediaControlOverlayEnclosureElement(document));
292 }
293
294 const AtomicString& MediaControlOverlayEnclosureElement::shadowPseudoId() const
295 {
296     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
297     return id;
298 }
299
300 // ----------------------------
301
302 MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document)
303     : MediaControlDivElement(document, MediaTimelineContainer)
304 {
305 }
306
307 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document* document)
308 {
309     RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document));
310     element->hide();
311     return element.release();
312 }
313
314 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
315 {
316     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container", AtomicString::ConstructFromLiteral));
317     return id;
318 }
319
320 void MediaControlTimelineContainerElement::setTimeDisplaysHidden(bool hidden)
321 {
322     for (unsigned i = 0; i < childNodeCount(); ++i) {
323         Node* child = childNode(i);
324         if (!child || !child->isElementNode())
325             continue;
326         Element* element = static_cast<Element*>(child);
327         if (element->shadowPseudoId() != getMediaControlTimeRemainingDisplayElementShadowPseudoId()
328             && element->shadowPseudoId() != getMediaControlCurrentTimeDisplayElementShadowPseudoId())
329             continue;
330
331         MediaControlTimeDisplayElement* timeDisplay = static_cast<MediaControlTimeDisplayElement*>(element);
332         if (hidden)
333             timeDisplay->hide();
334         else
335             timeDisplay->show();
336     }
337 }
338
339 RenderObject* MediaControlTimelineContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
340 {
341     return new (arena) RenderMediaControlTimelineContainer(this);
342 }
343
344 // ----------------------------
345
346 MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* document)
347     : MediaControlDivElement(document, MediaVolumeSliderContainer)
348 {
349 }
350
351 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document* document)
352 {
353     RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document));
354     element->hide();
355     return element.release();
356 }
357
358 RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
359 {
360     return new (arena) RenderMediaVolumeSliderContainer(this);
361 }
362
363 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
364 {
365     if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
366         return;
367
368     // Poor man's mouseleave event detection.
369     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
370     if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
371         return;
372
373     if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
374         return;
375
376     hide();
377 }
378
379 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
380 {
381     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container", AtomicString::ConstructFromLiteral));
382     return id;
383 }
384
385 // ----------------------------
386
387 MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document)
388     : MediaControlDivElement(document, MediaStatusDisplay)
389     , m_stateBeingDisplayed(Nothing)
390 {
391 }
392
393 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document* document)
394 {
395     RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(document));
396     element->hide();
397     return element.release();
398 }
399
400 void MediaControlStatusDisplayElement::update()
401 {
402     // Get the new state that we'll have to display.
403     StateBeingDisplayed newStateToDisplay = Nothing;
404
405     if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc())
406         newStateToDisplay = Loading;
407     else if (mediaController()->isLiveStream())
408         newStateToDisplay = LiveBroadcast;
409
410     if (newStateToDisplay == m_stateBeingDisplayed)
411         return;
412
413     if (m_stateBeingDisplayed == Nothing)
414         show();
415     else if (newStateToDisplay == Nothing)
416         hide();
417
418     m_stateBeingDisplayed = newStateToDisplay;
419
420     switch (m_stateBeingDisplayed) {
421     case Nothing:
422         setInnerText("", IGNORE_EXCEPTION);
423         break;
424     case Loading:
425         setInnerText(mediaElementLoadingStateText(), IGNORE_EXCEPTION);
426         break;
427     case LiveBroadcast:
428         setInnerText(mediaElementLiveBroadcastStateText(), IGNORE_EXCEPTION);
429         break;
430     }
431 }
432
433 const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
434 {
435     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display", AtomicString::ConstructFromLiteral));
436     return id;
437 }
438
439
440 // ----------------------------
441
442 MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls)
443     : MediaControlMuteButtonElement(document, MediaMuteButton)
444     , m_controls(controls)
445 {
446 }
447
448 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls)
449 {
450     ASSERT(controls);
451
452     RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
453     button->ensureUserAgentShadowRoot();
454     button->setType("button");
455     return button.release();
456 }
457
458 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
459 {
460     if (event->type() == eventNames().mouseoverEvent)
461         m_controls->showVolumeSlider();
462
463     MediaControlMuteButtonElement::defaultEventHandler(event);
464 }
465
466 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
467 {
468     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
469     return id;
470 }
471
472 // ----------------------------
473
474 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document)
475     : MediaControlMuteButtonElement(document, MediaMuteButton)
476 {
477 }
478
479 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document)
480 {
481     RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
482     button->ensureUserAgentShadowRoot();
483     button->setType("button");
484     return button.release();
485 }
486
487 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
488 {
489     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral));
490     return id;
491 }
492
493 // ----------------------------
494
495 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document)
496     : MediaControlInputElement(document, MediaPlayButton)
497 {
498 }
499
500 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document)
501 {
502     RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
503     button->ensureUserAgentShadowRoot();
504     button->setType("button");
505     return button.release();
506 }
507
508 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
509 {
510     if (event->type() == eventNames().clickEvent) {
511         if (mediaController()->canPlay())
512             mediaController()->play();
513         else
514             mediaController()->pause();
515         updateDisplayType();
516         event->setDefaultHandled();
517     }
518     HTMLInputElement::defaultEventHandler(event);
519 }
520
521 void MediaControlPlayButtonElement::updateDisplayType()
522 {
523     setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
524 }
525
526 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
527 {
528     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
529     return id;
530 }
531
532 // ----------------------------
533
534 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document)
535     : MediaControlInputElement(document, MediaOverlayPlayButton)
536 {
537 }
538
539 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document)
540 {
541     RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
542     button->ensureUserAgentShadowRoot();
543     button->setType("button");
544     return button.release();
545 }
546
547 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
548 {
549     if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
550         mediaController()->play();
551         updateDisplayType();
552         event->setDefaultHandled();
553     }
554     HTMLInputElement::defaultEventHandler(event);
555 }
556
557 void MediaControlOverlayPlayButtonElement::updateDisplayType()
558 {
559     if (mediaController()->canPlay()) {
560         show();
561     } else
562         hide();
563 }
564
565 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
566 {
567     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
568     return id;
569 }
570
571
572 // ----------------------------
573
574 MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document* document)
575     : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
576 {
577 }
578
579 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document* document)
580 {
581     RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document));
582     button->ensureUserAgentShadowRoot();
583     button->setType("button");
584     return button.release();
585 }
586
587 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
588 {
589     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button", AtomicString::ConstructFromLiteral));
590     return id;
591 }
592
593 // ----------------------------
594
595 MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document* document)
596     : MediaControlSeekButtonElement(document, MediaSeekBackButton)
597 {
598 }
599
600 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document* document)
601 {
602     RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document));
603     button->ensureUserAgentShadowRoot();
604     button->setType("button");
605     return button.release();
606 }
607
608 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
609 {
610     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button", AtomicString::ConstructFromLiteral));
611     return id;
612 }
613
614 // ----------------------------
615
616 MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document)
617     : MediaControlInputElement(document, MediaRewindButton)
618 {
619 }
620
621 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document* document)
622 {
623     RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document));
624     button->ensureUserAgentShadowRoot();
625     button->setType("button");
626     return button.release();
627 }
628
629 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
630 {
631     if (event->type() == eventNames().clickEvent) {
632         mediaController()->setCurrentTime(max(0.0, mediaController()->currentTime() - 30), IGNORE_EXCEPTION);
633         event->setDefaultHandled();
634     }
635     HTMLInputElement::defaultEventHandler(event);
636 }
637
638 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
639 {
640     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button", AtomicString::ConstructFromLiteral));
641     return id;
642 }
643
644 // ----------------------------
645
646 MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document)
647     : MediaControlInputElement(document, MediaReturnToRealtimeButton)
648 {
649 }
650
651 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document* document)
652 {
653     RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document));
654     button->ensureUserAgentShadowRoot();
655     button->setType("button");
656     button->hide();
657     return button.release();
658 }
659
660 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
661 {
662     if (event->type() == eventNames().clickEvent) {
663         mediaController()->returnToRealtime();
664         event->setDefaultHandled();
665     }
666     HTMLInputElement::defaultEventHandler(event);
667 }
668
669 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
670 {
671     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button", AtomicString::ConstructFromLiteral));
672     return id;
673 }
674
675 // ----------------------------
676
677 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document, MediaControls* controls)
678     : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
679 #if PLATFORM(MAC) || PLATFORM(WIN)
680     , m_controls(controls)
681 #endif
682 {
683 #if !PLATFORM(MAC) && !PLATFORM(WIN)
684     UNUSED_PARAM(controls);
685 #endif
686 }
687
688 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document, MediaControls* controls)
689 {
690     ASSERT(controls);
691
692     RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls));
693     button->ensureUserAgentShadowRoot();
694     button->setType("button");
695     button->hide();
696     return button.release();
697 }
698
699 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
700 {
701     bool captionsVisible = mediaController()->closedCaptionsVisible();
702     setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
703     setChecked(captionsVisible);
704 }
705
706 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
707 {
708     if (event->type() == eventNames().clickEvent) {
709         // FIXME: It's not great that the shared code is dictating behavior of platform-specific
710         // UI. Not all ports may want the closed captions button to toggle a list of tracks, so
711         // we have to use #if.
712         // https://bugs.webkit.org/show_bug.cgi?id=101877
713 #if !PLATFORM(MAC) && !PLATFORM(WIN)
714         mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
715         setChecked(mediaController()->closedCaptionsVisible());
716         updateDisplayType();
717 #else
718         m_controls->toggleClosedCaptionTrackList();
719 #endif
720         event->setDefaultHandled();
721     }
722
723     HTMLInputElement::defaultEventHandler(event);
724 }
725
726 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
727 {
728     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
729     return id;
730 }
731
732 // ----------------------------
733
734 MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement(Document* document)
735     : MediaControlDivElement(document, MediaClosedCaptionsContainer)
736 {
737 }
738
739 PassRefPtr<MediaControlClosedCaptionsContainerElement> MediaControlClosedCaptionsContainerElement::create(Document* document)
740 {
741     RefPtr<MediaControlClosedCaptionsContainerElement> element = adoptRef(new MediaControlClosedCaptionsContainerElement(document));
742     element->setAttribute(dirAttr, "auto");
743     element->hide();
744     return element.release();
745 }
746
747 const AtomicString& MediaControlClosedCaptionsContainerElement::shadowPseudoId() const
748 {
749     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-container", AtomicString::ConstructFromLiteral));
750     return id;
751 }
752
753 // ----------------------------
754
755 MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document* document, MediaControls* controls)
756     : MediaControlDivElement(document, MediaClosedCaptionsTrackList)
757     , m_controls(controls)
758 {
759 }
760
761 PassRefPtr<MediaControlClosedCaptionsTrackListElement> MediaControlClosedCaptionsTrackListElement::create(Document* document, MediaControls* controls)
762 {
763     ASSERT(controls);
764     RefPtr<MediaControlClosedCaptionsTrackListElement> element = adoptRef(new MediaControlClosedCaptionsTrackListElement(document, controls));
765     return element.release();
766 }
767
768 void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* event)
769 {
770 #if ENABLE(VIDEO_TRACK)
771     if (event->type() == eventNames().clickEvent) {
772         Node* target = event->target()->toNode();
773         if (!target || !target->isElementNode())
774             return;
775
776         // When we created the elements in the track list, we gave them a custom
777         // attribute representing the index in the HTMLMediaElement's list of tracks.
778         // Check if the event target has such a custom element and, if so,
779         // tell the HTMLMediaElement to enable that track.
780
781         RefPtr<TextTrack> textTrack;
782         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(toElement(target));
783         if (iter != m_menuToTrackMap.end())
784             textTrack = iter->value;
785         m_menuToTrackMap.clear();
786         m_controls->toggleClosedCaptionTrackList();
787         if (!textTrack)
788             return;
789
790         HTMLMediaElement* mediaElement = toParentMediaElement(this);
791         if (!mediaElement)
792             return;
793
794         mediaElement->setSelectedTextTrack(textTrack.get());
795
796         updateDisplay();
797     }
798
799     MediaControlDivElement::defaultEventHandler(event);
800 #endif
801 }
802
803 const AtomicString& MediaControlClosedCaptionsTrackListElement::shadowPseudoId() const
804 {
805     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-track-list", AtomicString::ConstructFromLiteral));
806     return id;
807 }
808
809 void MediaControlClosedCaptionsTrackListElement::updateDisplay()
810 {
811 #if ENABLE(VIDEO_TRACK)
812     DEFINE_STATIC_LOCAL(AtomicString, selectedClassValue, ("selected", AtomicString::ConstructFromLiteral));
813
814     if (!mediaController()->hasClosedCaptions())
815         return;
816
817     if (!document()->page())
818         return;
819     CaptionUserPreferences::CaptionDisplayMode displayMode = document()->page()->group().captionPreferences()->captionDisplayMode();
820
821     HTMLMediaElement* mediaElement = toParentMediaElement(this);
822     if (!mediaElement)
823         return;
824
825     TextTrackList* trackList = mediaElement->textTracks();
826     if (!trackList || !trackList->length())
827         return;
828
829     rebuildTrackListMenu();
830
831     RefPtr<Element> offMenuItem;
832     bool trackMenuItemSelected = false;
833
834     for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) {
835         RefPtr<Element> trackItem = m_menuItems[i];
836
837         RefPtr<TextTrack> textTrack;
838         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(trackItem.get());
839         if (iter == m_menuToTrackMap.end())
840             continue;
841         textTrack = iter->value;
842         if (!textTrack)
843             continue;
844
845         if (textTrack == TextTrack::captionMenuOffItem()) {
846             offMenuItem = trackItem;
847             continue;
848         }
849
850         if (textTrack == TextTrack::captionMenuAutomaticItem()) {
851             if (displayMode == CaptionUserPreferences::Automatic)
852                 trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
853             else
854                 trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
855             continue;
856         }
857
858         if (displayMode != CaptionUserPreferences::Automatic && textTrack->mode() == TextTrack::showingKeyword()) {
859             trackMenuItemSelected = true;
860             trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
861         } else
862             trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
863     }
864
865     if (offMenuItem) {
866         if (displayMode == CaptionUserPreferences::ForcedOnly && !trackMenuItemSelected)
867             offMenuItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
868         else
869             offMenuItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
870     }
871 #endif
872 }
873
874 void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
875 {
876 #if ENABLE(VIDEO_TRACK)
877     // Remove any existing content.
878     removeChildren();
879     m_menuItems.clear();
880     m_menuToTrackMap.clear();
881
882     if (!mediaController()->hasClosedCaptions())
883         return;
884
885     HTMLMediaElement* mediaElement = toParentMediaElement(this);
886     if (!mediaElement)
887         return;
888
889     TextTrackList* trackList = mediaElement->textTracks();
890     if (!trackList || !trackList->length())
891         return;
892
893     Document* doc = document();
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 = doc->createElement(h3Tag, ASSERT_NO_EXCEPTION);
900     captionsHeader->appendChild(doc->createTextNode(textTrackSubtitlesText()));
901     appendChild(captionsHeader);
902     RefPtr<Element> captionsMenuList = doc->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 = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
907         menuItem->appendChild(doc->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, String::number(m_videoDisplaySize.size().width()) + "px");
1306             setInlineStyleProperty(CSSPropertyHeight, String::number(m_videoDisplaySize.size().height()) + "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, String::number(m_videoDisplaySize.size().width()) + "px");
1321         setInlineStyleProperty(CSSPropertyHeight, String::number(m_videoDisplaySize.size().height()) + "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     RenderObject* renderer = this->renderer();
1401     if (!renderer)
1402         return 0;
1403
1404     Frame* frame = document()->frame();
1405     if (!frame)
1406         return 0;
1407
1408     document()->updateLayout();
1409
1410     LayoutRect topLevelRect;
1411     IntRect paintingRect = pixelSnappedIntRect(renderer->paintingRootRect(topLevelRect));
1412
1413     float deviceScaleFactor = 1;
1414     if (Page* page = document()->page())
1415         deviceScaleFactor = page->deviceScaleFactor();
1416
1417     OwnPtr<ImageBuffer> buffer(ImageBuffer::create(paintingRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB));
1418     if (!buffer)
1419         return 0;
1420
1421     // Translate the renderer painting rect into graphics context coordinates.
1422     FloatSize translation(-paintingRect.x(), -paintingRect.y());
1423
1424     buffer->context()->translate(translation);
1425
1426     RenderLayer* layer = frame->contentRenderer()->layer();
1427     layer->paint(buffer->context(), paintingRect, PaintBehaviorFlattenCompositingLayers, renderer, 0, RenderLayer::PaintLayerPaintingCompositingAllPhases);
1428
1429     return buffer->copyImage();
1430 }
1431
1432 void MediaControlTextTrackContainerElement::textTrackRepresentationBoundsChanged(const IntRect&)
1433 {
1434     updateSizes();
1435 }
1436
1437 #endif // ENABLE(VIDEO_TRACK)
1438
1439 // ----------------------------
1440
1441 } // namespace WebCore
1442
1443 #endif // ENABLE(VIDEO)