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 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 72 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document& document) 73 { 74 return adoptRef(new MediaControlPanelElement(document)); 75 } 76 77 const AtomicString& MediaControlPanelElement::pseudo() const 78 { 79 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral)); 80 return id; 81 } 82 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 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 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 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 142 void MediaControlPanelElement::stopTimer() 143 { 144 if (m_transitionTimer.isActive()) 145 m_transitionTimer.stop(); 146 } 147 148 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*) 149 { 150 if (!m_opaque) 151 hide(); 152 153 stopTimer(); 154 } 155 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 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 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 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 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 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 242 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed) 243 { 244 m_isDisplayed = isDisplayed; 245 } 246 247 // ---------------------------- 248 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 255 PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document& document) 256 { 257 return adoptRef(new MediaControlPanelEnclosureElement(document)); 258 } 259 260 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 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 274 PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document& document) 275 { 276 return adoptRef(new MediaControlOverlayEnclosureElement(document)); 277 } 278 279 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 287 MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document& document, MediaControls* controls) 288 : MediaControlMuteButtonElement(document, MediaMuteButton) 289 , m_controls(controls) 290 { 291 } 292 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 303 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event) 304 { 305 if (event->type() == EventTypeNames::mouseover) 306 m_controls->showVolumeSlider(); 307 308 MediaControlMuteButtonElement::defaultEventHandler(event); 309 } 310 311 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 319 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document& document) 320 : MediaControlMuteButtonElement(document, MediaMuteButton) 321 { 322 } 323 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 332 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 340 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document& document) 341 : MediaControlInputElement(document, MediaPlayButton) 342 { 343 } 344 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 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 366 void MediaControlPlayButtonElement::updateDisplayType() 367 { 368 setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton); 369 } 370 371 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 379 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document& document) 380 : MediaControlInputElement(document, MediaOverlayPlayButton) 381 { 382 } 383 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 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 402 void MediaControlOverlayPlayButtonElement::updateDisplayType() 403 { 404 if (mediaController()->canPlay()) { 405 show(); 406 } else 407 hide(); 408 } 409 410 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 419 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document& document, MediaControls*) 420 : MediaControlInputElement(document, MediaShowClosedCaptionsButton) 421 { 422 } 423 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 435 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() 436 { 437 bool captionsVisible = mediaController()->closedCaptionsVisible(); 438 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); 439 setChecked(captionsVisible); 440 } 441 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 454 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 462 MediaControlTimelineElement::MediaControlTimelineElement(Document& document, MediaControls* controls) 463 : MediaControlInputElement(document, MediaSlider) 464 , m_controls(controls) 465 { 466 } 467 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 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 508 bool MediaControlTimelineElement::willRespondToMouseClickEvents() 509 { 510 return inDocument() && document().isActive(); 511 } 512 513 void MediaControlTimelineElement::setPosition(double currentTime) 514 { 515 setValue(String::number(currentTime)); 516 } 517 518 void MediaControlTimelineElement::setDuration(double duration) 519 { 520 setFloatingPointAttribute(maxAttr, std::isfinite(duration) ? duration : 0); 521 } 522 523 524 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 532 MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document& document) 533 : MediaControlVolumeSliderElement(document) 534 { 535 } 536 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 547 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 555 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document& document) 556 : MediaControlInputElement(document, MediaEnterFullscreenButton) 557 { 558 } 559 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 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 589 const AtomicString& MediaControlFullscreenButtonElement::pseudo() const 590 { 591 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral)); 592 return id; 593 } 594 595 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen) 596 { 597 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton); 598 } 599 600 // ---------------------------- 601 602 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document& document) 603 : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay) 604 { 605 } 606 607 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document& document) 608 { 609 return adoptRef(new MediaControlTimeRemainingDisplayElement(document)); 610 } 611 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 618 const AtomicString& MediaControlTimeRemainingDisplayElement::pseudo() const 619 { 620 return getMediaControlTimeRemainingDisplayElementShadowPseudoId(); 621 } 622 623 // ---------------------------- 624 625 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document& document) 626 : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay) 627 { 628 } 629 630 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document& document) 631 { 632 return adoptRef(new MediaControlCurrentTimeDisplayElement(document)); 633 } 634 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 641 const AtomicString& MediaControlCurrentTimeDisplayElement::pseudo() const 642 { 643 return getMediaControlCurrentTimeDisplayElementShadowPseudoId(); 644 } 645 646 // ---------------------------- 647 648 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document& document) 649 : MediaControlDivElement(document, MediaTextTrackDisplayContainer) 650 , m_fontSize(0) 651 { 652 } 653 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 661 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderStyle*) 662 { 663 return new RenderTextTrackContainerElement(this); 664 } 665 666 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId() 667 { 668 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral)); 669 return id; 670 } 671 672 const AtomicString& MediaControlTextTrackContainerElement::pseudo() const 673 { 674 return textTrackContainerElementShadowPseudoId(); 675 } 676 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 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