1 /*
2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30
31 #if ENABLE(VIDEO)
32
33 #include "MediaControlElements.h"
34
35 #include "CSSStyleSelector.h"
36 #include "EventNames.h"
37 #include "FloatConversion.h"
38 #include "Frame.h"
39 #include "HTMLNames.h"
40 #include "LocalizedStrings.h"
41 #include "MediaControls.h"
42 #include "MouseEvent.h"
43 #include "Page.h"
44 #include "RenderFlexibleBox.h"
45 #include "RenderMedia.h"
46 #include "RenderSlider.h"
47 #include "RenderTheme.h"
48 #include "RenderView.h"
49 #include "Settings.h"
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
toParentMediaElement(RenderObject * o)55 HTMLMediaElement* toParentMediaElement(RenderObject* o)
56 {
57 Node* node = o->node();
58 Node* mediaNode = node ? node->shadowAncestorNode() : 0;
59 if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
60 return 0;
61
62 return static_cast<HTMLMediaElement*>(mediaNode);
63 }
64
65 // FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
66 static const float cSeekRepeatDelay = 0.1f;
67 static const float cStepTime = 0.07f;
68 static const float cSeekTime = 0.2f;
69
70 // ----------------------------
71
MediaControlElement(HTMLMediaElement * mediaElement)72 MediaControlElement::MediaControlElement(HTMLMediaElement* mediaElement)
73 : HTMLDivElement(divTag, mediaElement->document())
74 , m_mediaElement(mediaElement)
75 {
76 }
77
displayString()78 static const String& displayString()
79 {
80 DEFINE_STATIC_LOCAL(String, s, ("display"));
81 return s;
82 }
83
show()84 void MediaControlElement::show()
85 {
86 ExceptionCode ec;
87 // FIXME: Make more efficient <http://webkit.org/b/58157>
88 style()->removeProperty(displayString(), ec);
89 }
90
hide()91 void MediaControlElement::hide()
92 {
93 ExceptionCode ec;
94 // FIXME: Make more efficient <http://webkit.org/b/58157>
95 DEFINE_STATIC_LOCAL(String, none, ("none"));
96 style()->setProperty(displayString(), none, ec);
97 }
98
99 // ----------------------------
100
MediaControlPanelElement(HTMLMediaElement * mediaElement)101 inline MediaControlPanelElement::MediaControlPanelElement(HTMLMediaElement* mediaElement)
102 : MediaControlElement(mediaElement)
103 {
104 }
105
create(HTMLMediaElement * mediaElement)106 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(HTMLMediaElement* mediaElement)
107 {
108 return adoptRef(new MediaControlPanelElement(mediaElement));
109 }
110
displayType() const111 MediaControlElementType MediaControlPanelElement::displayType() const
112 {
113 return MediaControlsPanel;
114 }
115
shadowPseudoId() const116 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
117 {
118 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel"));
119 return id;
120 }
121
122 // ----------------------------
123
MediaControlTimelineContainerElement(HTMLMediaElement * mediaElement)124 inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(HTMLMediaElement* mediaElement)
125 : MediaControlElement(mediaElement)
126 {
127 }
128
create(HTMLMediaElement * mediaElement)129 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(HTMLMediaElement* mediaElement)
130 {
131 RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(mediaElement));
132 element->hide();
133 return element.release();
134 }
135
displayType() const136 MediaControlElementType MediaControlTimelineContainerElement::displayType() const
137 {
138 return MediaTimelineContainer;
139 }
140
shadowPseudoId() const141 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
142 {
143 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container"));
144 return id;
145 }
146
147 // ----------------------------
148
149 class RenderMediaVolumeSliderContainer : public RenderBlock {
150 public:
151 RenderMediaVolumeSliderContainer(Node*);
152
153 private:
154 virtual void layout();
155 };
156
RenderMediaVolumeSliderContainer(Node * node)157 RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node)
158 : RenderBlock(node)
159 {
160 }
161
layout()162 void RenderMediaVolumeSliderContainer::layout()
163 {
164 RenderBlock::layout();
165 if (style()->display() == NONE || !previousSibling() || !previousSibling()->isBox())
166 return;
167
168 RenderBox* buttonBox = toRenderBox(previousSibling());
169
170 if (view())
171 view()->disableLayoutState();
172
173 IntPoint offset = theme()->volumeSliderOffsetFromMuteButton(buttonBox, IntSize(width(), height()));
174 setX(offset.x() + buttonBox->offsetLeft());
175 setY(offset.y() + buttonBox->offsetTop());
176
177 if (view())
178 view()->enableLayoutState();
179 }
180
MediaControlVolumeSliderContainerElement(HTMLMediaElement * mediaElement)181 inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(HTMLMediaElement* mediaElement)
182 : MediaControlElement(mediaElement)
183 {
184 }
185
create(HTMLMediaElement * mediaElement)186 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement)
187 {
188 RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement));
189 element->hide();
190 return element.release();
191 }
192
createRenderer(RenderArena * arena,RenderStyle *)193 RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
194 {
195 return new (arena) RenderMediaVolumeSliderContainer(this);
196 }
197
defaultEventHandler(Event * event)198 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
199 {
200 if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
201 return;
202
203 // Poor man's mouseleave event detection.
204 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
205 if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
206 return;
207
208 if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
209 return;
210
211 hide();
212 }
213
214
displayType() const215 MediaControlElementType MediaControlVolumeSliderContainerElement::displayType() const
216 {
217 return MediaVolumeSliderContainer;
218 }
219
shadowPseudoId() const220 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
221 {
222 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container"));
223 return id;
224 }
225
226 // ----------------------------
227
MediaControlStatusDisplayElement(HTMLMediaElement * mediaElement)228 inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(HTMLMediaElement* mediaElement)
229 : MediaControlElement(mediaElement)
230 , m_stateBeingDisplayed(Nothing)
231 {
232 }
233
create(HTMLMediaElement * mediaElement)234 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(HTMLMediaElement* mediaElement)
235 {
236 RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(mediaElement));
237 element->hide();
238 return element.release();
239 }
240
update()241 void MediaControlStatusDisplayElement::update()
242 {
243 // Get the new state that we'll have to display.
244 StateBeingDisplayed newStateToDisplay = Nothing;
245
246 if (mediaElement()->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA && !mediaElement()->currentSrc().isEmpty())
247 newStateToDisplay = Loading;
248 else if (mediaElement()->movieLoadType() == MediaPlayer::LiveStream)
249 newStateToDisplay = LiveBroadcast;
250
251 if (newStateToDisplay == m_stateBeingDisplayed)
252 return;
253
254 ExceptionCode e;
255
256 if (m_stateBeingDisplayed == Nothing)
257 show();
258 else if (newStateToDisplay == Nothing)
259 hide();
260
261 m_stateBeingDisplayed = newStateToDisplay;
262
263 switch (m_stateBeingDisplayed) {
264 case Nothing:
265 setInnerText("", e);
266 break;
267 case Loading:
268 setInnerText(mediaElementLoadingStateText(), e);
269 break;
270 case LiveBroadcast:
271 setInnerText(mediaElementLiveBroadcastStateText(), e);
272 break;
273 }
274 }
275
displayType() const276 MediaControlElementType MediaControlStatusDisplayElement::displayType() const
277 {
278 return MediaStatusDisplay;
279 }
280
shadowPseudoId() const281 const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
282 {
283 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display"));
284 return id;
285 }
286
287 // ----------------------------
288
MediaControlInputElement(HTMLMediaElement * mediaElement,MediaControlElementType displayType)289 MediaControlInputElement::MediaControlInputElement(HTMLMediaElement* mediaElement, MediaControlElementType displayType)
290 : HTMLInputElement(inputTag, mediaElement->document(), 0, false)
291 , m_mediaElement(mediaElement)
292 , m_displayType(displayType)
293 {
294 }
295
show()296 void MediaControlInputElement::show()
297 {
298 ExceptionCode ec;
299 style()->removeProperty(displayString(), ec);
300 }
301
hide()302 void MediaControlInputElement::hide()
303 {
304 ExceptionCode ec;
305 DEFINE_STATIC_LOCAL(String, none, ("none"));
306 style()->setProperty(displayString(), none, ec);
307 }
308
309
setDisplayType(MediaControlElementType displayType)310 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
311 {
312 if (displayType == m_displayType)
313 return;
314
315 m_displayType = displayType;
316 if (RenderObject* object = renderer())
317 object->repaint();
318 }
319
320 // ----------------------------
321
MediaControlMuteButtonElement(HTMLMediaElement * mediaElement,MediaControlElementType displayType)322 inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(HTMLMediaElement* mediaElement, MediaControlElementType displayType)
323 : MediaControlInputElement(mediaElement, displayType)
324 {
325 }
326
defaultEventHandler(Event * event)327 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
328 {
329 if (event->type() == eventNames().clickEvent) {
330 mediaElement()->setMuted(!mediaElement()->muted());
331 event->setDefaultHandled();
332 }
333
334 HTMLInputElement::defaultEventHandler(event);
335 }
336
changedMute()337 void MediaControlMuteButtonElement::changedMute()
338 {
339 updateDisplayType();
340 }
341
updateDisplayType()342 void MediaControlMuteButtonElement::updateDisplayType()
343 {
344 setDisplayType(mediaElement()->muted() ? MediaUnMuteButton : MediaMuteButton);
345 }
346
347 // ----------------------------
348
MediaControlPanelMuteButtonElement(HTMLMediaElement * mediaElement,MediaControls * controls)349 inline MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(HTMLMediaElement* mediaElement, MediaControls* controls)
350 : MediaControlMuteButtonElement(mediaElement, MediaMuteButton)
351 , m_controls(controls)
352 {
353 }
354
create(HTMLMediaElement * mediaElement,MediaControls * controls)355 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(HTMLMediaElement* mediaElement, MediaControls* controls)
356 {
357 ASSERT(controls);
358
359 RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(mediaElement, controls));
360 button->setType("button");
361 return button.release();
362 }
363
defaultEventHandler(Event * event)364 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
365 {
366 if (event->type() == eventNames().mouseoverEvent)
367 m_controls->showVolumeSlider();
368
369 MediaControlMuteButtonElement::defaultEventHandler(event);
370 }
371
shadowPseudoId() const372 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
373 {
374 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button"));
375 return id;
376 }
377
378 // ----------------------------
379
MediaControlVolumeSliderMuteButtonElement(HTMLMediaElement * mediaElement)380 inline MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(HTMLMediaElement* mediaElement)
381 : MediaControlMuteButtonElement(mediaElement, MediaMuteButton)
382 {
383 }
384
create(HTMLMediaElement * mediaElement)385 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(HTMLMediaElement* mediaElement)
386 {
387 RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(mediaElement));
388 button->setType("button");
389 return button.release();
390 }
391
shadowPseudoId() const392 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
393 {
394 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button"));
395 return id;
396 }
397
398 // ----------------------------
399
MediaControlPlayButtonElement(HTMLMediaElement * mediaElement)400 inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(HTMLMediaElement* mediaElement)
401 : MediaControlInputElement(mediaElement, MediaPlayButton)
402 {
403 }
404
create(HTMLMediaElement * mediaElement)405 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement)
406 {
407 RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(mediaElement));
408 button->setType("button");
409 return button.release();
410 }
411
defaultEventHandler(Event * event)412 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
413 {
414 if (event->type() == eventNames().clickEvent) {
415 mediaElement()->togglePlayState();
416 updateDisplayType();
417 event->setDefaultHandled();
418 }
419 HTMLInputElement::defaultEventHandler(event);
420 }
421
updateDisplayType()422 void MediaControlPlayButtonElement::updateDisplayType()
423 {
424 setDisplayType(mediaElement()->canPlay() ? MediaPlayButton : MediaPauseButton);
425 }
426
shadowPseudoId() const427 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
428 {
429 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button"));
430 return id;
431 }
432
433 // ----------------------------
434
MediaControlSeekButtonElement(HTMLMediaElement * mediaElement,MediaControlElementType displayType)435 inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(HTMLMediaElement* mediaElement, MediaControlElementType displayType)
436 : MediaControlInputElement(mediaElement, displayType)
437 , m_seeking(false)
438 , m_capturing(false)
439 , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
440 {
441 }
442
defaultEventHandler(Event * event)443 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
444 {
445 if (event->type() == eventNames().mousedownEvent) {
446 if (Frame* frame = document()->frame()) {
447 m_capturing = true;
448 frame->eventHandler()->setCapturingMouseEventsNode(this);
449 }
450 mediaElement()->pause(event->fromUserGesture());
451 m_seekTimer.startRepeating(cSeekRepeatDelay);
452 event->setDefaultHandled();
453 } else if (event->type() == eventNames().mouseupEvent) {
454 if (m_capturing)
455 if (Frame* frame = document()->frame()) {
456 m_capturing = false;
457 frame->eventHandler()->setCapturingMouseEventsNode(0);
458 }
459 ExceptionCode ec;
460 if (m_seeking || m_seekTimer.isActive()) {
461 if (!m_seeking) {
462 float stepTime = isForwardButton() ? cStepTime : -cStepTime;
463 mediaElement()->setCurrentTime(mediaElement()->currentTime() + stepTime, ec);
464 }
465 m_seekTimer.stop();
466 m_seeking = false;
467 event->setDefaultHandled();
468 }
469 }
470 HTMLInputElement::defaultEventHandler(event);
471 }
472
seekTimerFired(Timer<MediaControlSeekButtonElement> *)473 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
474 {
475 ExceptionCode ec;
476 m_seeking = true;
477 float seekTime = isForwardButton() ? cSeekTime : -cSeekTime;
478 mediaElement()->setCurrentTime(mediaElement()->currentTime() + seekTime, ec);
479 }
480
detach()481 void MediaControlSeekButtonElement::detach()
482 {
483 if (m_capturing) {
484 if (Frame* frame = document()->frame())
485 frame->eventHandler()->setCapturingMouseEventsNode(0);
486 }
487 MediaControlInputElement::detach();
488 }
489
490 // ----------------------------
491
MediaControlSeekForwardButtonElement(HTMLMediaElement * mediaElement)492 inline MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(HTMLMediaElement* mediaElement)
493 : MediaControlSeekButtonElement(mediaElement, MediaSeekForwardButton)
494 {
495 }
496
create(HTMLMediaElement * mediaElement)497 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(HTMLMediaElement* mediaElement)
498 {
499 RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(mediaElement));
500 button->setType("button");
501 return button.release();
502 }
503
shadowPseudoId() const504 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
505 {
506 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button"));
507 return id;
508 }
509
510 // ----------------------------
511
MediaControlSeekBackButtonElement(HTMLMediaElement * mediaElement)512 inline MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(HTMLMediaElement* mediaElement)
513 : MediaControlSeekButtonElement(mediaElement, MediaSeekBackButton)
514 {
515 }
516
create(HTMLMediaElement * mediaElement)517 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(HTMLMediaElement* mediaElement)
518 {
519 RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(mediaElement));
520 button->setType("button");
521 return button.release();
522 }
523
shadowPseudoId() const524 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
525 {
526 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button"));
527 return id;
528 }
529
530 // ----------------------------
531
MediaControlRewindButtonElement(HTMLMediaElement * element)532 inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(HTMLMediaElement* element)
533 : MediaControlInputElement(element, MediaRewindButton)
534 {
535 }
536
create(HTMLMediaElement * mediaElement)537 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement)
538 {
539 RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(mediaElement));
540 button->setType("button");
541 return button.release();
542 }
543
defaultEventHandler(Event * event)544 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
545 {
546 if (event->type() == eventNames().clickEvent) {
547 mediaElement()->rewind(30);
548 event->setDefaultHandled();
549 }
550 HTMLInputElement::defaultEventHandler(event);
551 }
552
shadowPseudoId() const553 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
554 {
555 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button"));
556 return id;
557 }
558
559 // ----------------------------
560
MediaControlReturnToRealtimeButtonElement(HTMLMediaElement * mediaElement)561 inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(HTMLMediaElement* mediaElement)
562 : MediaControlInputElement(mediaElement, MediaReturnToRealtimeButton)
563 {
564 }
565
create(HTMLMediaElement * mediaElement)566 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement)
567 {
568 RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(mediaElement));
569 button->setType("button");
570 button->hide();
571 return button.release();
572 }
573
defaultEventHandler(Event * event)574 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
575 {
576 if (event->type() == eventNames().clickEvent) {
577 mediaElement()->returnToRealtime();
578 event->setDefaultHandled();
579 }
580 HTMLInputElement::defaultEventHandler(event);
581 }
582
shadowPseudoId() const583 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
584 {
585 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button"));
586 return id;
587 }
588
589 // ----------------------------
590
MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement * mediaElement)591 inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement* mediaElement)
592 : MediaControlInputElement(mediaElement, MediaShowClosedCaptionsButton)
593 {
594 }
595
create(HTMLMediaElement * mediaElement)596 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement)
597 {
598 RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(mediaElement));
599 button->setType("button");
600 button->hide();
601 return button.release();
602 }
603
defaultEventHandler(Event * event)604 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
605 {
606 if (event->type() == eventNames().clickEvent) {
607 mediaElement()->setClosedCaptionsVisible(!mediaElement()->closedCaptionsVisible());
608 setChecked(mediaElement()->closedCaptionsVisible());
609 updateDisplayType();
610 event->setDefaultHandled();
611 }
612
613 HTMLInputElement::defaultEventHandler(event);
614 }
615
updateDisplayType()616 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
617 {
618 setDisplayType(mediaElement()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
619 }
620
shadowPseudoId() const621 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
622 {
623 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button"));
624 return id;
625 }
626
627 // ----------------------------
628
MediaControlTimelineElement(HTMLMediaElement * mediaElement,MediaControls * controls)629 MediaControlTimelineElement::MediaControlTimelineElement(HTMLMediaElement* mediaElement, MediaControls* controls)
630 : MediaControlInputElement(mediaElement, MediaSlider)
631 , m_controls(controls)
632 {
633 }
634
create(HTMLMediaElement * mediaElement,MediaControls * controls)635 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(HTMLMediaElement* mediaElement, MediaControls* controls)
636 {
637 ASSERT(controls);
638
639 RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(mediaElement, controls));
640 timeline->setType("range");
641 timeline->setAttribute(precisionAttr, "float");
642 return timeline.release();
643 }
644
defaultEventHandler(Event * event)645 void MediaControlTimelineElement::defaultEventHandler(Event* event)
646 {
647 // Left button is 0. Rejects mouse events not from left button.
648 if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
649 return;
650
651 if (!attached())
652 return;
653
654 if (event->type() == eventNames().mousedownEvent)
655 mediaElement()->beginScrubbing();
656
657 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
658 if (event->type() == eventNames().touchstartEvent)
659 mediaElement()->beginScrubbing();
660 #endif
661
662 MediaControlInputElement::defaultEventHandler(event);
663
664 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
665 return;
666
667 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
668 if (event->type() == eventNames().touchmoveEvent || event->type() == eventNames().touchcancelEvent)
669 return;
670 #endif
671
672 float time = narrowPrecisionToFloat(value().toDouble());
673 if (time != mediaElement()->currentTime()) {
674 // FIXME: This is fired 3 times on every click. We should not be doing that <http:/webkit.org/b/58160>.
675 ExceptionCode ec;
676 mediaElement()->setCurrentTime(time, ec);
677 }
678
679 RenderSlider* slider = toRenderSlider(renderer());
680 if (slider && slider->inDragMode())
681 m_controls->updateTimeDisplay();
682
683 if (event->type() == eventNames().mouseupEvent)
684 mediaElement()->endScrubbing();
685
686 #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
687 if (event->type() == eventNames().touchendEvent)
688 mediaElement()->endScrubbing();
689 #endif
690 }
691
setPosition(float currentTime)692 void MediaControlTimelineElement::setPosition(float currentTime)
693 {
694 setValue(String::number(currentTime));
695 }
696
setDuration(float duration)697 void MediaControlTimelineElement::setDuration(float duration)
698 {
699 setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
700 }
701
702
shadowPseudoId() const703 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
704 {
705 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline"));
706 return id;
707 }
708
709 // ----------------------------
710
MediaControlVolumeSliderElement(HTMLMediaElement * mediaElement)711 inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(HTMLMediaElement* mediaElement)
712 : MediaControlInputElement(mediaElement, MediaVolumeSlider)
713 {
714 }
715
create(HTMLMediaElement * mediaElement)716 PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement)
717 {
718 RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(mediaElement));
719 slider->setType("range");
720 slider->setAttribute(precisionAttr, "float");
721 slider->setAttribute(maxAttr, "1");
722 slider->setAttribute(valueAttr, String::number(mediaElement->volume()));
723 return slider.release();
724 }
725
defaultEventHandler(Event * event)726 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
727 {
728 // Left button is 0. Rejects mouse events not from left button.
729 if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
730 return;
731
732 if (!attached())
733 return;
734
735 MediaControlInputElement::defaultEventHandler(event);
736
737 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
738 return;
739
740 float volume = narrowPrecisionToFloat(value().toDouble());
741 if (volume != mediaElement()->volume()) {
742 ExceptionCode ec = 0;
743 mediaElement()->setVolume(volume, ec);
744 ASSERT(!ec);
745 }
746 }
747
setVolume(float volume)748 void MediaControlVolumeSliderElement::setVolume(float volume)
749 {
750 if (value().toFloat() != volume)
751 setValue(String::number(volume));
752 }
753
shadowPseudoId() const754 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const
755 {
756 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider"));
757 return id;
758 }
759
760 // ----------------------------
761
MediaControlFullscreenVolumeSliderElement(HTMLMediaElement * mediaElement)762 inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(HTMLMediaElement* mediaElement)
763 : MediaControlVolumeSliderElement(mediaElement)
764 {
765 }
766
create(HTMLMediaElement * mediaElement)767 PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(HTMLMediaElement* mediaElement)
768 {
769 RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(mediaElement));
770 slider->setType("range");
771 slider->setAttribute(precisionAttr, "float");
772 slider->setAttribute(maxAttr, "1");
773 slider->setAttribute(valueAttr, String::number(mediaElement->volume()));
774 return slider.release();
775 }
776
shadowPseudoId() const777 const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
778 {
779 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider"));
780 return id;
781 }
782
783 // ----------------------------
784
MediaControlFullscreenButtonElement(HTMLMediaElement * mediaElement,MediaControls * controls)785 inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(HTMLMediaElement* mediaElement, MediaControls* controls)
786 : MediaControlInputElement(mediaElement, MediaFullscreenButton)
787 , m_controls(controls)
788 {
789 }
790
create(HTMLMediaElement * mediaElement,MediaControls * controls)791 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement, MediaControls* controls)
792 {
793 ASSERT(controls);
794
795 RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(mediaElement, controls));
796 button->setType("button");
797 button->hide();
798 return button.release();
799 }
800
defaultEventHandler(Event * event)801 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
802 {
803 if (event->type() == eventNames().clickEvent) {
804 #if ENABLE(FULLSCREEN_API)
805 // Only use the new full screen API if the fullScreenEnabled setting has
806 // been explicitly enabled. Otherwise, use the old fullscreen API. This
807 // allows apps which embed a WebView to retain the existing full screen
808 // video implementation without requiring them to implement their own full
809 // screen behavior.
810 if (document()->settings() && document()->settings()->fullScreenEnabled()) {
811 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == mediaElement()) {
812 document()->webkitCancelFullScreen();
813 m_controls->exitedFullscreen();
814 } else {
815 mediaElement()->webkitRequestFullScreen(0);
816 m_controls->enteredFullscreen();
817 }
818 } else
819 #endif
820 mediaElement()->enterFullscreen();
821 event->setDefaultHandled();
822 }
823 HTMLInputElement::defaultEventHandler(event);
824 }
825
shadowPseudoId() const826 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
827 {
828 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button"));
829 return id;
830 }
831
832 // ----------------------------
833
MediaControlFullscreenVolumeMinButtonElement(HTMLMediaElement * mediaElement)834 inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(HTMLMediaElement* mediaElement)
835 : MediaControlInputElement(mediaElement, MediaUnMuteButton)
836 {
837 }
838
create(HTMLMediaElement * mediaElement)839 PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(HTMLMediaElement* mediaElement)
840 {
841 RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(mediaElement));
842 button->setType("button");
843 return button.release();
844 }
845
defaultEventHandler(Event * event)846 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
847 {
848 if (event->type() == eventNames().clickEvent) {
849 ExceptionCode code = 0;
850 mediaElement()->setVolume(0, code);
851 event->setDefaultHandled();
852 }
853 HTMLInputElement::defaultEventHandler(event);
854 }
855
shadowPseudoId() const856 const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
857 {
858 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button"));
859 return id;
860 }
861
862 // ----------------------------
863
MediaControlFullscreenVolumeMaxButtonElement(HTMLMediaElement * mediaElement)864 inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(HTMLMediaElement* mediaElement)
865 : MediaControlInputElement(mediaElement, MediaMuteButton)
866 {
867 }
868
create(HTMLMediaElement * mediaElement)869 PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(HTMLMediaElement* mediaElement)
870 {
871 RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(mediaElement));
872 button->setType("button");
873 return button.release();
874 }
875
defaultEventHandler(Event * event)876 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
877 {
878 if (event->type() == eventNames().clickEvent) {
879 ExceptionCode code = 0;
880 mediaElement()->setVolume(1, code);
881 event->setDefaultHandled();
882 }
883 HTMLInputElement::defaultEventHandler(event);
884 }
885
shadowPseudoId() const886 const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
887 {
888 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button"));
889 return id;
890 }
891
892 // ----------------------------
893
894 class RenderMediaControlTimeDisplay : public RenderFlexibleBox {
895 public:
896 RenderMediaControlTimeDisplay(Node*);
897
898 private:
899 virtual void layout();
900 };
901
RenderMediaControlTimeDisplay(Node * node)902 RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node)
903 : RenderFlexibleBox(node)
904 {
905 }
906
907 // We want the timeline slider to be at least 100 pixels wide.
908 // FIXME: Eliminate hard-coded widths altogether.
909 static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45;
910
layout()911 void RenderMediaControlTimeDisplay::layout()
912 {
913 RenderFlexibleBox::layout();
914 RenderBox* timelineContainerBox = parentBox();
915 while (timelineContainerBox && timelineContainerBox->isAnonymous())
916 timelineContainerBox = timelineContainerBox->parentBox();
917
918 if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays)
919 setWidth(0);
920 }
921
MediaControlTimeDisplayElement(HTMLMediaElement * mediaElement)922 inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement)
923 : MediaControlElement(mediaElement)
924 , m_currentValue(0)
925 {
926 }
927
setCurrentValue(float time)928 void MediaControlTimeDisplayElement::setCurrentValue(float time)
929 {
930 m_currentValue = time;
931 }
932
createRenderer(RenderArena * arena,RenderStyle *)933 RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*)
934 {
935 return new (arena) RenderMediaControlTimeDisplay(this);
936 }
937
938 // ----------------------------
939
create(HTMLMediaElement * mediaElement)940 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(HTMLMediaElement* mediaElement)
941 {
942 return adoptRef(new MediaControlTimeRemainingDisplayElement(mediaElement));
943 }
944
MediaControlTimeRemainingDisplayElement(HTMLMediaElement * mediaElement)945 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(HTMLMediaElement* mediaElement)
946 : MediaControlTimeDisplayElement(mediaElement)
947 {
948 }
949
displayType() const950 MediaControlElementType MediaControlTimeRemainingDisplayElement::displayType() const
951 {
952 return MediaTimeRemainingDisplay;
953 }
954
shadowPseudoId() const955 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
956 {
957 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display"));
958 return id;
959 }
960
961 // ----------------------------
962
create(HTMLMediaElement * mediaElement)963 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(HTMLMediaElement* mediaElement)
964 {
965 return adoptRef(new MediaControlCurrentTimeDisplayElement(mediaElement));
966 }
967
MediaControlCurrentTimeDisplayElement(HTMLMediaElement * mediaElement)968 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(HTMLMediaElement* mediaElement)
969 : MediaControlTimeDisplayElement(mediaElement)
970 {
971 }
972
displayType() const973 MediaControlElementType MediaControlCurrentTimeDisplayElement::displayType() const
974 {
975 return MediaCurrentTimeDisplay;
976 }
977
shadowPseudoId() const978 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
979 {
980 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display"));
981 return id;
982 }
983
984 } // namespace WebCore
985
986 #endif // ENABLE(VIDEO)
987