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 #include "core/html/shadow/MediaControlElements.h"
32
33 #include "RuntimeEnabledFeatures.h"
34 #include "bindings/v8/ExceptionStatePlaceholder.h"
35 #include "core/dom/DOMTokenList.h"
36 #include "core/dom/FullscreenElementStack.h"
37 #include "core/dom/shadow/ShadowRoot.h"
38 #include "core/events/MouseEvent.h"
39 #include "core/events/ThreadLocalEventNames.h"
40 #include "core/html/HTMLVideoElement.h"
41 #include "core/html/shadow/MediaControls.h"
42 #include "core/html/track/TextTrack.h"
43 #include "core/html/track/vtt/VTTRegionList.h"
44 #include "core/page/EventHandler.h"
45 #include "core/frame/Frame.h"
46 #include "core/frame/Settings.h"
47 #include "core/rendering/RenderMediaControlElements.h"
48 #include "core/rendering/RenderSlider.h"
49 #include "core/rendering/RenderTheme.h"
50 #include "core/rendering/RenderVideo.h"
51
52 namespace WebCore {
53
54 using namespace HTMLNames;
55
56 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
57 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
58
59 static const double fadeInDuration = 0.1;
60 static const double fadeOutDuration = 0.3;
61
MediaControlPanelElement(Document & document)62 MediaControlPanelElement::MediaControlPanelElement(Document& document)
63 : MediaControlDivElement(document, MediaControlsPanel)
64 , m_canBeDragged(false)
65 , m_isBeingDragged(false)
66 , m_isDisplayed(false)
67 , m_opaque(true)
68 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
69 {
70 }
71
create(Document & document)72 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document& document)
73 {
74 return adoptRef(new MediaControlPanelElement(document));
75 }
76
pseudo() const77 const AtomicString& MediaControlPanelElement::pseudo() const
78 {
79 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral));
80 return id;
81 }
82
startDrag(const LayoutPoint & eventLocation)83 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
84 {
85 if (!m_canBeDragged)
86 return;
87
88 if (m_isBeingDragged)
89 return;
90
91 RenderObject* renderer = this->renderer();
92 if (!renderer || !renderer->isBox())
93 return;
94
95 Frame* frame = document().frame();
96 if (!frame)
97 return;
98
99 m_lastDragEventLocation = eventLocation;
100
101 frame->eventHandler().setCapturingMouseEventsNode(this);
102
103 m_isBeingDragged = true;
104 }
105
continueDrag(const LayoutPoint & eventLocation)106 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
107 {
108 if (!m_isBeingDragged)
109 return;
110
111 LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation;
112 m_cumulativeDragOffset.move(distanceDragged);
113 m_lastDragEventLocation = eventLocation;
114 setPosition(m_cumulativeDragOffset);
115 }
116
endDrag()117 void MediaControlPanelElement::endDrag()
118 {
119 if (!m_isBeingDragged)
120 return;
121
122 m_isBeingDragged = false;
123
124 Frame* frame = document().frame();
125 if (!frame)
126 return;
127
128 frame->eventHandler().setCapturingMouseEventsNode(0);
129 }
130
startTimer()131 void MediaControlPanelElement::startTimer()
132 {
133 stopTimer();
134
135 // The timer is required to set the property display:'none' on the panel,
136 // such that captions are correctly displayed at the bottom of the video
137 // at the end of the fadeout transition.
138 // FIXME: Racing a transition with a setTimeout like this is wrong.
139 m_transitionTimer.startOneShot(fadeOutDuration);
140 }
141
stopTimer()142 void MediaControlPanelElement::stopTimer()
143 {
144 if (m_transitionTimer.isActive())
145 m_transitionTimer.stop();
146 }
147
transitionTimerFired(Timer<MediaControlPanelElement> *)148 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
149 {
150 if (!m_opaque)
151 hide();
152
153 stopTimer();
154 }
155
setPosition(const LayoutPoint & position)156 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
157 {
158 double left = position.x();
159 double top = position.y();
160
161 // Set the left and top to control the panel's position; this depends on it being absolute positioned.
162 // Set the margin to zero since the position passed in will already include the effect of the margin.
163 setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
164 setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
165 setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
166 setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
167
168 classList()->add("dragged", IGNORE_EXCEPTION);
169 }
170
resetPosition()171 void MediaControlPanelElement::resetPosition()
172 {
173 removeInlineStyleProperty(CSSPropertyLeft);
174 removeInlineStyleProperty(CSSPropertyTop);
175 removeInlineStyleProperty(CSSPropertyMarginLeft);
176 removeInlineStyleProperty(CSSPropertyMarginTop);
177
178 classList()->remove("dragged", IGNORE_EXCEPTION);
179
180 m_cumulativeDragOffset.setX(0);
181 m_cumulativeDragOffset.setY(0);
182 }
183
makeOpaque()184 void MediaControlPanelElement::makeOpaque()
185 {
186 if (m_opaque)
187 return;
188
189 setInlineStyleProperty(CSSPropertyTransitionProperty, CSSPropertyOpacity);
190 setInlineStyleProperty(CSSPropertyTransitionDuration, fadeInDuration, CSSPrimitiveValue::CSS_S);
191 setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
192
193 m_opaque = true;
194
195 if (m_isDisplayed)
196 show();
197 }
198
makeTransparent()199 void MediaControlPanelElement::makeTransparent()
200 {
201 if (!m_opaque)
202 return;
203
204 setInlineStyleProperty(CSSPropertyTransitionProperty, CSSPropertyOpacity);
205 setInlineStyleProperty(CSSPropertyTransitionDuration, fadeOutDuration, CSSPrimitiveValue::CSS_S);
206 setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
207
208 m_opaque = false;
209 startTimer();
210 }
211
defaultEventHandler(Event * event)212 void MediaControlPanelElement::defaultEventHandler(Event* event)
213 {
214 MediaControlDivElement::defaultEventHandler(event);
215
216 if (event->isMouseEvent()) {
217 LayoutPoint location = toMouseEvent(event)->absoluteLocation();
218 if (event->type() == EventTypeNames::mousedown && event->target() == this) {
219 startDrag(location);
220 event->setDefaultHandled();
221 } else if (event->type() == EventTypeNames::mousemove && m_isBeingDragged)
222 continueDrag(location);
223 else if (event->type() == EventTypeNames::mouseup && m_isBeingDragged) {
224 continueDrag(location);
225 endDrag();
226 event->setDefaultHandled();
227 }
228 }
229 }
230
setCanBeDragged(bool canBeDragged)231 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
232 {
233 if (m_canBeDragged == canBeDragged)
234 return;
235
236 m_canBeDragged = canBeDragged;
237
238 if (!canBeDragged)
239 endDrag();
240 }
241
setIsDisplayed(bool isDisplayed)242 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
243 {
244 m_isDisplayed = isDisplayed;
245 }
246
247 // ----------------------------
248
MediaControlPanelEnclosureElement(Document & document)249 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(Document& document)
250 // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
251 : MediaControlDivElement(document, MediaControlsPanel)
252 {
253 }
254
create(Document & document)255 PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document& document)
256 {
257 return adoptRef(new MediaControlPanelEnclosureElement(document));
258 }
259
pseudo() const260 const AtomicString& MediaControlPanelEnclosureElement::pseudo() const
261 {
262 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
263 return id;
264 }
265
266 // ----------------------------
267
MediaControlOverlayEnclosureElement(Document & document)268 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document& document)
269 // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
270 : MediaControlDivElement(document, MediaControlsPanel)
271 {
272 }
273
create(Document & document)274 PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document& document)
275 {
276 return adoptRef(new MediaControlOverlayEnclosureElement(document));
277 }
278
pseudo() const279 const AtomicString& MediaControlOverlayEnclosureElement::pseudo() const
280 {
281 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
282 return id;
283 }
284
285 // ----------------------------
286
MediaControlPanelMuteButtonElement(Document & document,MediaControls * controls)287 MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document& document, MediaControls* controls)
288 : MediaControlMuteButtonElement(document, MediaMuteButton)
289 , m_controls(controls)
290 {
291 }
292
create(Document & document,MediaControls * controls)293 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document& document, MediaControls* controls)
294 {
295 ASSERT(controls);
296
297 RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
298 button->ensureUserAgentShadowRoot();
299 button->setType("button");
300 return button.release();
301 }
302
defaultEventHandler(Event * event)303 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
304 {
305 if (event->type() == EventTypeNames::mouseover)
306 m_controls->showVolumeSlider();
307
308 MediaControlMuteButtonElement::defaultEventHandler(event);
309 }
310
pseudo() const311 const AtomicString& MediaControlPanelMuteButtonElement::pseudo() const
312 {
313 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
314 return id;
315 }
316
317 // ----------------------------
318
MediaControlVolumeSliderMuteButtonElement(Document & document)319 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document& document)
320 : MediaControlMuteButtonElement(document, MediaMuteButton)
321 {
322 }
323
create(Document & document)324 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document& document)
325 {
326 RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
327 button->ensureUserAgentShadowRoot();
328 button->setType("button");
329 return button.release();
330 }
331
pseudo() const332 const AtomicString& MediaControlVolumeSliderMuteButtonElement::pseudo() const
333 {
334 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral));
335 return id;
336 }
337
338 // ----------------------------
339
MediaControlPlayButtonElement(Document & document)340 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document& document)
341 : MediaControlInputElement(document, MediaPlayButton)
342 {
343 }
344
create(Document & document)345 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document& document)
346 {
347 RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
348 button->ensureUserAgentShadowRoot();
349 button->setType("button");
350 return button.release();
351 }
352
defaultEventHandler(Event * event)353 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
354 {
355 if (event->type() == EventTypeNames::click) {
356 if (mediaController()->canPlay())
357 mediaController()->play();
358 else
359 mediaController()->pause();
360 updateDisplayType();
361 event->setDefaultHandled();
362 }
363 HTMLInputElement::defaultEventHandler(event);
364 }
365
updateDisplayType()366 void MediaControlPlayButtonElement::updateDisplayType()
367 {
368 setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
369 }
370
pseudo() const371 const AtomicString& MediaControlPlayButtonElement::pseudo() const
372 {
373 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
374 return id;
375 }
376
377 // ----------------------------
378
MediaControlOverlayPlayButtonElement(Document & document)379 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document& document)
380 : MediaControlInputElement(document, MediaOverlayPlayButton)
381 {
382 }
383
create(Document & document)384 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document& document)
385 {
386 RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
387 button->ensureUserAgentShadowRoot();
388 button->setType("button");
389 return button.release();
390 }
391
defaultEventHandler(Event * event)392 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
393 {
394 if (event->type() == EventTypeNames::click && mediaController()->canPlay()) {
395 mediaController()->play();
396 updateDisplayType();
397 event->setDefaultHandled();
398 }
399 HTMLInputElement::defaultEventHandler(event);
400 }
401
updateDisplayType()402 void MediaControlOverlayPlayButtonElement::updateDisplayType()
403 {
404 if (mediaController()->canPlay()) {
405 show();
406 } else
407 hide();
408 }
409
pseudo() const410 const AtomicString& MediaControlOverlayPlayButtonElement::pseudo() const
411 {
412 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
413 return id;
414 }
415
416
417 // ----------------------------
418
MediaControlToggleClosedCaptionsButtonElement(Document & document,MediaControls *)419 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document& document, MediaControls*)
420 : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
421 {
422 }
423
create(Document & document,MediaControls * controls)424 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document& document, MediaControls* controls)
425 {
426 ASSERT(controls);
427
428 RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls));
429 button->ensureUserAgentShadowRoot();
430 button->setType("button");
431 button->hide();
432 return button.release();
433 }
434
updateDisplayType()435 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
436 {
437 bool captionsVisible = mediaController()->closedCaptionsVisible();
438 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
439 setChecked(captionsVisible);
440 }
441
defaultEventHandler(Event * event)442 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
443 {
444 if (event->type() == EventTypeNames::click) {
445 mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
446 setChecked(mediaController()->closedCaptionsVisible());
447 updateDisplayType();
448 event->setDefaultHandled();
449 }
450
451 HTMLInputElement::defaultEventHandler(event);
452 }
453
pseudo() const454 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::pseudo() const
455 {
456 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
457 return id;
458 }
459
460 // ----------------------------
461
MediaControlTimelineElement(Document & document,MediaControls * controls)462 MediaControlTimelineElement::MediaControlTimelineElement(Document& document, MediaControls* controls)
463 : MediaControlInputElement(document, MediaSlider)
464 , m_controls(controls)
465 {
466 }
467
create(Document & document,MediaControls * controls)468 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document& document, MediaControls* controls)
469 {
470 ASSERT(controls);
471
472 RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
473 timeline->ensureUserAgentShadowRoot();
474 timeline->setType("range");
475 timeline->setAttribute(stepAttr, "any");
476 return timeline.release();
477 }
478
defaultEventHandler(Event * event)479 void MediaControlTimelineElement::defaultEventHandler(Event* event)
480 {
481 // Left button is 0. Rejects mouse events not from left button.
482 if (event->isMouseEvent() && toMouseEvent(event)->button())
483 return;
484
485 if (!inDocument() || !document().isActive())
486 return;
487
488 if (event->type() == EventTypeNames::mousedown)
489 mediaController()->beginScrubbing();
490
491 if (event->type() == EventTypeNames::mouseup)
492 mediaController()->endScrubbing();
493
494 MediaControlInputElement::defaultEventHandler(event);
495
496 if (event->type() == EventTypeNames::mouseover || event->type() == EventTypeNames::mouseout || event->type() == EventTypeNames::mousemove)
497 return;
498
499 double time = value().toDouble();
500 if (event->type() == EventTypeNames::input && time != mediaController()->currentTime())
501 mediaController()->setCurrentTime(time, IGNORE_EXCEPTION);
502
503 RenderSlider* slider = toRenderSlider(renderer());
504 if (slider && slider->inDragMode())
505 m_controls->updateCurrentTimeDisplay();
506 }
507
willRespondToMouseClickEvents()508 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
509 {
510 return inDocument() && document().isActive();
511 }
512
setPosition(double currentTime)513 void MediaControlTimelineElement::setPosition(double currentTime)
514 {
515 setValue(String::number(currentTime));
516 }
517
setDuration(double duration)518 void MediaControlTimelineElement::setDuration(double duration)
519 {
520 setFloatingPointAttribute(maxAttr, std::isfinite(duration) ? duration : 0);
521 }
522
523
pseudo() const524 const AtomicString& MediaControlTimelineElement::pseudo() const
525 {
526 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral));
527 return id;
528 }
529
530 // ----------------------------
531
MediaControlPanelVolumeSliderElement(Document & document)532 MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document& document)
533 : MediaControlVolumeSliderElement(document)
534 {
535 }
536
create(Document & document)537 PassRefPtr<MediaControlPanelVolumeSliderElement> MediaControlPanelVolumeSliderElement::create(Document& document)
538 {
539 RefPtr<MediaControlPanelVolumeSliderElement> slider = adoptRef(new MediaControlPanelVolumeSliderElement(document));
540 slider->ensureUserAgentShadowRoot();
541 slider->setType("range");
542 slider->setAttribute(stepAttr, "any");
543 slider->setAttribute(maxAttr, "1");
544 return slider.release();
545 }
546
pseudo() const547 const AtomicString& MediaControlPanelVolumeSliderElement::pseudo() const
548 {
549 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral));
550 return id;
551 }
552
553 // ----------------------------
554
MediaControlFullscreenButtonElement(Document & document)555 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document& document)
556 : MediaControlInputElement(document, MediaEnterFullscreenButton)
557 {
558 }
559
create(Document & document)560 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document& document)
561 {
562 RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document));
563 button->ensureUserAgentShadowRoot();
564 button->setType("button");
565 button->hide();
566 return button.release();
567 }
568
defaultEventHandler(Event * event)569 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
570 {
571 if (event->type() == EventTypeNames::click) {
572 // Only use the new full screen API if the fullScreenEnabled setting has
573 // been explicitly enabled. Otherwise, use the old fullscreen API. This
574 // allows apps which embed a WebView to retain the existing full screen
575 // video implementation without requiring them to implement their own full
576 // screen behavior.
577 if (document().settings() && document().settings()->fullScreenEnabled()) {
578 if (FullscreenElementStack::isActiveFullScreenElement(toParentMediaElement(this)))
579 FullscreenElementStack::from(&document())->webkitCancelFullScreen();
580 else
581 FullscreenElementStack::from(&document())->requestFullScreenForElement(toParentMediaElement(this), 0, FullscreenElementStack::ExemptIFrameAllowFullScreenRequirement);
582 } else
583 mediaController()->enterFullscreen();
584 event->setDefaultHandled();
585 }
586 HTMLInputElement::defaultEventHandler(event);
587 }
588
pseudo() const589 const AtomicString& MediaControlFullscreenButtonElement::pseudo() const
590 {
591 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral));
592 return id;
593 }
594
setIsFullscreen(bool isFullscreen)595 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
596 {
597 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
598 }
599
600 // ----------------------------
601
MediaControlTimeRemainingDisplayElement(Document & document)602 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document& document)
603 : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay)
604 {
605 }
606
create(Document & document)607 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document& document)
608 {
609 return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
610 }
611
getMediaControlTimeRemainingDisplayElementShadowPseudoId()612 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId()
613 {
614 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral));
615 return id;
616 }
617
pseudo() const618 const AtomicString& MediaControlTimeRemainingDisplayElement::pseudo() const
619 {
620 return getMediaControlTimeRemainingDisplayElementShadowPseudoId();
621 }
622
623 // ----------------------------
624
MediaControlCurrentTimeDisplayElement(Document & document)625 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document& document)
626 : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay)
627 {
628 }
629
create(Document & document)630 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document& document)
631 {
632 return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
633 }
634
getMediaControlCurrentTimeDisplayElementShadowPseudoId()635 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId()
636 {
637 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral));
638 return id;
639 }
640
pseudo() const641 const AtomicString& MediaControlCurrentTimeDisplayElement::pseudo() const
642 {
643 return getMediaControlCurrentTimeDisplayElementShadowPseudoId();
644 }
645
646 // ----------------------------
647
MediaControlTextTrackContainerElement(Document & document)648 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document& document)
649 : MediaControlDivElement(document, MediaTextTrackDisplayContainer)
650 , m_fontSize(0)
651 {
652 }
653
create(Document & document)654 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document& document)
655 {
656 RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
657 element->hide();
658 return element.release();
659 }
660
createRenderer(RenderStyle *)661 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderStyle*)
662 {
663 return new RenderTextTrackContainerElement(this);
664 }
665
textTrackContainerElementShadowPseudoId()666 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId()
667 {
668 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral));
669 return id;
670 }
671
pseudo() const672 const AtomicString& MediaControlTextTrackContainerElement::pseudo() const
673 {
674 return textTrackContainerElementShadowPseudoId();
675 }
676
updateDisplay()677 void MediaControlTextTrackContainerElement::updateDisplay()
678 {
679 if (!mediaController()->closedCaptionsVisible()) {
680 removeChildren();
681 return;
682 }
683
684 HTMLMediaElement* mediaElement = toParentMediaElement(this);
685 // 1. If the media element is an audio element, or is another playback
686 // mechanism with no rendering area, abort these steps. There is nothing to
687 // render.
688 if (!mediaElement || !mediaElement->isVideo())
689 return;
690
691 // 2. Let video be the media element or other playback mechanism.
692 HTMLVideoElement* video = toHTMLVideoElement(mediaElement);
693
694 // 3. Let output be an empty list of absolutely positioned CSS block boxes.
695 Vector<RefPtr<HTMLDivElement> > output;
696
697 // 4. If the user agent is exposing a user interface for video, add to
698 // output one or more completely transparent positioned CSS block boxes that
699 // cover the same region as the user interface.
700
701 // 5. If the last time these rules were run, the user agent was not exposing
702 // a user interface for video, but now it is, let reset be true. Otherwise,
703 // let reset be false.
704
705 // There is nothing to be done explicitly for 4th and 5th steps, as
706 // everything is handled through CSS. The caption box is on top of the
707 // controls box, in a container set with the -webkit-box display property.
708
709 // 6. Let tracks be the subset of video's list of text tracks that have as
710 // their rules for updating the text track rendering these rules for
711 // updating the display of WebVTT text tracks, and whose text track mode is
712 // showing or showing by default.
713 // 7. Let cues be an empty list of text track cues.
714 // 8. For each track track in tracks, append to cues all the cues from
715 // track's list of cues that have their text track cue active flag set.
716 CueList activeCues = video->currentlyActiveCues();
717
718 // 9. If reset is false, then, for each text track cue cue in cues: if cue's
719 // text track cue display state has a set of CSS boxes, then add those boxes
720 // to output, and remove cue from cues.
721
722 // There is nothing explicitly to be done here, as all the caching occurs
723 // within the TextTrackCue instance itself. If parameters of the cue change,
724 // the display tree is cleared.
725
726 // 10. For each text track cue cue in cues that has not yet had
727 // corresponding CSS boxes added to output, in text track cue order, run the
728 // following substeps:
729 for (size_t i = 0; i < activeCues.size(); ++i) {
730 TextTrackCue* cue = activeCues[i].data();
731
732 ASSERT(cue->isActive());
733 if (!cue->track() || !cue->track()->isRendered() || !cue->isActive())
734 continue;
735
736 cue->updateDisplay(m_videoDisplaySize.size(), *this);
737 }
738
739 // 11. Return output.
740 if (hasChildNodes())
741 show();
742 else
743 hide();
744 }
745
updateSizes(bool forceUpdate)746 void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
747 {
748 HTMLMediaElement* mediaElement = toParentMediaElement(this);
749 if (!mediaElement)
750 return;
751
752 if (!document().isActive())
753 return;
754
755 IntRect videoBox;
756
757 if (!mediaElement->renderer() || !mediaElement->renderer()->isVideo())
758 return;
759 videoBox = toRenderVideo(mediaElement->renderer())->videoBox();
760
761 if (!forceUpdate && m_videoDisplaySize == videoBox)
762 return;
763 m_videoDisplaySize = videoBox;
764
765 float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
766
767 float fontSize = smallestDimension * 0.05f;
768 if (fontSize != m_fontSize) {
769 m_fontSize = fontSize;
770 setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX);
771 }
772 }
773
774 // ----------------------------
775
776 } // namespace WebCore
777