OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. |
3 * Copyright (C) 2012 Google Inc. All rights reserved. | 3 * Copyright (C) 2012 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * | 8 * |
9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 14 matching lines...) Expand all Loading... |
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 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 | 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. | 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 */ | 28 */ |
29 | 29 |
30 #include "core/html/shadow/MediaControlElements.h" | 30 #include "core/html/shadow/MediaControlElements.h" |
31 | 31 |
32 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | 32 #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
33 #include "core/InputTypeNames.h" | 33 #include "core/InputTypeNames.h" |
34 #include "core/dom/ClientRect.h" | 34 #include "core/dom/ClientRect.h" |
| 35 #include "core/dom/Text.h" |
35 #include "core/dom/shadow/ShadowRoot.h" | 36 #include "core/dom/shadow/ShadowRoot.h" |
36 #include "core/events/MouseEvent.h" | 37 #include "core/events/MouseEvent.h" |
37 #include "core/frame/LocalFrame.h" | 38 #include "core/frame/LocalFrame.h" |
| 39 #include "core/html/HTMLLabelElement.h" |
38 #include "core/html/HTMLMediaSource.h" | 40 #include "core/html/HTMLMediaSource.h" |
| 41 #include "core/html/HTMLSpanElement.h" |
39 #include "core/html/HTMLVideoElement.h" | 42 #include "core/html/HTMLVideoElement.h" |
40 #include "core/html/TimeRanges.h" | 43 #include "core/html/TimeRanges.h" |
41 #include "core/html/shadow/MediaControls.h" | 44 #include "core/html/shadow/MediaControls.h" |
| 45 #include "core/html/track/TextTrackList.h" |
42 #include "core/input/EventHandler.h" | 46 #include "core/input/EventHandler.h" |
43 #include "core/layout/LayoutTheme.h" | 47 #include "core/layout/LayoutTheme.h" |
44 #include "core/layout/LayoutVideo.h" | 48 #include "core/layout/LayoutVideo.h" |
45 #include "core/layout/api/LayoutSliderItem.h" | 49 #include "core/layout/api/LayoutSliderItem.h" |
| 50 #include "platform/EventDispatchForbiddenScope.h" |
46 #include "platform/Histogram.h" | 51 #include "platform/Histogram.h" |
47 #include "platform/RuntimeEnabledFeatures.h" | 52 #include "platform/RuntimeEnabledFeatures.h" |
| 53 #include "platform/text/PlatformLocale.h" |
48 #include "public/platform/Platform.h" | 54 #include "public/platform/Platform.h" |
49 #include "public/platform/UserMetricsAction.h" | 55 #include "public/platform/UserMetricsAction.h" |
50 | 56 |
51 namespace blink { | 57 namespace blink { |
52 | 58 |
53 using namespace HTMLNames; | 59 using namespace HTMLNames; |
54 | 60 |
55 namespace { | 61 namespace { |
56 | 62 |
57 // This is the duration from mediaControls.css | 63 // This is the duration from mediaControls.css |
58 const double fadeOutDuration = 0.3; | 64 const double fadeOutDuration = 0.3; |
59 | 65 |
| 66 const QualifiedName& trackIndexAttrName() |
| 67 { |
| 68 // Save the track index in an attribute to avoid holding a pointer to the te
xt track. |
| 69 DEFINE_STATIC_LOCAL(QualifiedName, trackIndexAttr, (nullAtom, "data-track-in
dex", nullAtom)); |
| 70 return trackIndexAttr; |
| 71 } |
| 72 |
| 73 // When specified as trackIndex, disable text tracks. |
| 74 const int trackIndexOffValue = -1; |
| 75 |
60 bool isUserInteractionEvent(Event* event) | 76 bool isUserInteractionEvent(Event* event) |
61 { | 77 { |
62 const AtomicString& type = event->type(); | 78 const AtomicString& type = event->type(); |
63 return type == EventTypeNames::mousedown | 79 return type == EventTypeNames::mousedown |
64 || type == EventTypeNames::mouseup | 80 || type == EventTypeNames::mouseup |
65 || type == EventTypeNames::click | 81 || type == EventTypeNames::click |
66 || type == EventTypeNames::dblclick | 82 || type == EventTypeNames::dblclick |
67 || event->isKeyboardEvent() | 83 || event->isKeyboardEvent() |
68 || event->isTouchEvent(); | 84 || event->isTouchEvent(); |
69 } | 85 } |
(...skipping 19 matching lines...) Expand all Loading... |
89 | 105 |
90 Element* elementFromCenter(Element& element) | 106 Element* elementFromCenter(Element& element) |
91 { | 107 { |
92 ClientRect* clientRect = element.getBoundingClientRect(); | 108 ClientRect* clientRect = element.getBoundingClientRect(); |
93 int centerX = static_cast<int>((clientRect->left() + clientRect->right()) /
2); | 109 int centerX = static_cast<int>((clientRect->left() + clientRect->right()) /
2); |
94 int centerY = static_cast<int>((clientRect->top() + clientRect->bottom()) /
2); | 110 int centerY = static_cast<int>((clientRect->top() + clientRect->bottom()) /
2); |
95 | 111 |
96 return element.document().elementFromPoint(centerX , centerY); | 112 return element.document().elementFromPoint(centerX , centerY); |
97 } | 113 } |
98 | 114 |
| 115 bool hasDuplicateLabel(TextTrack* currentTrack) |
| 116 { |
| 117 DCHECK(currentTrack); |
| 118 TextTrackList* trackList = currentTrack->trackList(); |
| 119 // The runtime of this method is quadratic but since there are usually very
few text tracks it won't |
| 120 // affect the performance much. |
| 121 String currentTrackLabel = currentTrack->label(); |
| 122 for (unsigned i = 0; i < trackList->length(); i++) { |
| 123 TextTrack* track = trackList->anonymousIndexedGetter(i); |
| 124 if (currentTrack != track && currentTrackLabel == track->label()) |
| 125 return true; |
| 126 } |
| 127 return false; |
| 128 } |
| 129 |
99 } // anonymous namespace | 130 } // anonymous namespace |
100 | 131 |
101 MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls) | 132 MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls) |
102 : MediaControlDivElement(mediaControls, MediaControlsPanel) | 133 : MediaControlDivElement(mediaControls, MediaControlsPanel) |
103 , m_isDisplayed(false) | 134 , m_isDisplayed(false) |
104 , m_opaque(true) | 135 , m_opaque(true) |
105 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired) | 136 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired) |
106 { | 137 { |
107 } | 138 } |
108 | 139 |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg
leClosedCaptionsButtonElement(mediaControls); | 395 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg
leClosedCaptionsButtonElement(mediaControls); |
365 button->ensureUserAgentShadowRoot(); | 396 button->ensureUserAgentShadowRoot(); |
366 button->setType(InputTypeNames::button); | 397 button->setType(InputTypeNames::button); |
367 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed
-captions-button")); | 398 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed
-captions-button")); |
368 button->setIsWanted(false); | 399 button->setIsWanted(false); |
369 return button; | 400 return button; |
370 } | 401 } |
371 | 402 |
372 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() | 403 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() |
373 { | 404 { |
374 bool captionsVisible = mediaElement().closedCaptionsVisible(); | 405 bool captionsVisible = mediaElement().textTracksVisible(); |
375 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl
osedCaptionsButton); | 406 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl
osedCaptionsButton); |
376 setChecked(captionsVisible); | |
377 } | 407 } |
378 | 408 |
379 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e
vent) | 409 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e
vent) |
380 { | 410 { |
381 if (event->type() == EventTypeNames::click) { | 411 if (event->type() == EventTypeNames::click) { |
382 if (mediaElement().closedCaptionsVisible()) | 412 mediaControls().toggleTextTrackList(); |
383 Platform::current()->recordAction(UserMetricsAction("Media.Controls.
ClosedCaptionHide")); | |
384 else | |
385 Platform::current()->recordAction(UserMetricsAction("Media.Controls.
ClosedCaptionShow")); | |
386 mediaElement().setClosedCaptionsVisible(!mediaElement().closedCaptionsVi
sible()); | |
387 setChecked(mediaElement().closedCaptionsVisible()); | |
388 updateDisplayType(); | 413 updateDisplayType(); |
389 event->setDefaultHandled(); | 414 event->setDefaultHandled(); |
390 } | 415 } |
391 | 416 |
392 HTMLInputElement::defaultEventHandler(event); | 417 HTMLInputElement::defaultEventHandler(event); |
393 } | 418 } |
394 | 419 |
395 // ---------------------------- | 420 // ---------------------------- |
396 | 421 |
| 422 MediaControlTextTrackListElement::MediaControlTextTrackListElement(MediaControls
& mediaControls) |
| 423 : MediaControlDivElement(mediaControls, MediaTextTrackList) |
| 424 { |
| 425 } |
| 426 |
| 427 MediaControlTextTrackListElement* MediaControlTextTrackListElement::create(Media
Controls& mediaControls) |
| 428 { |
| 429 MediaControlTextTrackListElement* element = new MediaControlTextTrackListEle
ment(mediaControls); |
| 430 element->setShadowPseudoId(AtomicString("-internal-media-controls-text-track
-list")); |
| 431 element->setIsWanted(false); |
| 432 return element; |
| 433 } |
| 434 |
| 435 void MediaControlTextTrackListElement::defaultEventHandler(Event* event) |
| 436 { |
| 437 if (event->type() == EventTypeNames::change) { |
| 438 // Identify which input element was selected and set track to showing |
| 439 Node* target = event->target()->toNode(); |
| 440 if (!target || !target->isElementNode()) |
| 441 return; |
| 442 |
| 443 disableShowingTextTracks(); |
| 444 int trackIndex = toElement(target)->getIntegralAttribute(trackIndexAttrN
ame()); |
| 445 if (trackIndex != trackIndexOffValue) { |
| 446 ASSERT(trackIndex >= 0); |
| 447 showTextTrackAtIndex(trackIndex); |
| 448 mediaElement().disableAutomaticTextTrackSelection(); |
| 449 } |
| 450 |
| 451 mediaControls().toggleTextTrackList(); |
| 452 event->setDefaultHandled(); |
| 453 } |
| 454 MediaControlDivElement::defaultEventHandler(event); |
| 455 } |
| 456 |
| 457 void MediaControlTextTrackListElement::setVisible(bool visible) |
| 458 { |
| 459 if (visible) { |
| 460 setIsWanted(true); |
| 461 refreshTextTrackListMenu(); |
| 462 } else { |
| 463 setIsWanted(false); |
| 464 } |
| 465 } |
| 466 |
| 467 void MediaControlTextTrackListElement::showTextTrackAtIndex(unsigned indexToEnab
le) |
| 468 { |
| 469 TextTrackList* trackList = mediaElement().textTracks(); |
| 470 if (indexToEnable >= trackList->length()) |
| 471 return; |
| 472 TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable); |
| 473 if (track && track->canBeRendered()) |
| 474 track->setMode(TextTrack::showingKeyword()); |
| 475 } |
| 476 |
| 477 void MediaControlTextTrackListElement::disableShowingTextTracks() |
| 478 { |
| 479 TextTrackList* trackList = mediaElement().textTracks(); |
| 480 for (unsigned i = 0; i < trackList->length(); ++i) { |
| 481 TextTrack* track = trackList->anonymousIndexedGetter(i); |
| 482 if (track->mode() == TextTrack::showingKeyword()) |
| 483 track->setMode(TextTrack::disabledKeyword()); |
| 484 } |
| 485 } |
| 486 |
| 487 String MediaControlTextTrackListElement::getTextTrackLabel(TextTrack* track) |
| 488 { |
| 489 if (!track) |
| 490 return mediaElement().locale().queryString(WebLocalizedString::TextTrack
sOff); |
| 491 |
| 492 String trackLabel = track->label(); |
| 493 |
| 494 if (trackLabel.isEmpty()) |
| 495 trackLabel = String(mediaElement().locale().queryString(WebLocalizedStri
ng::TextTracksNoLabel)); |
| 496 |
| 497 return trackLabel; |
| 498 } |
| 499 |
| 500 // TextTrack parameter when passed in as a nullptr, creates the "Off" list item
in the track list. |
| 501 Element* MediaControlTextTrackListElement::createTextTrackListItem(TextTrack* tr
ack) |
| 502 { |
| 503 int trackIndex = track ? track->trackIndex() : trackIndexOffValue; |
| 504 HTMLLabelElement* trackItem = HTMLLabelElement::create(document(), nullptr); |
| 505 trackItem->setShadowPseudoId(AtomicString("-internal-media-controls-text-tra
ck-list-item")); |
| 506 HTMLInputElement* trackItemInput = HTMLInputElement::create(document(), null
ptr, false); |
| 507 trackItemInput->setShadowPseudoId(AtomicString("-internal-media-controls-tex
t-track-list-item-input")); |
| 508 trackItemInput->setType(InputTypeNames::checkbox); |
| 509 trackItemInput->setIntegralAttribute(trackIndexAttrName(), trackIndex); |
| 510 if (!mediaElement().textTracksVisible()) { |
| 511 if (!track) |
| 512 trackItemInput->setChecked(true); |
| 513 } else { |
| 514 // If there are multiple text tracks set to showing, they must all have |
| 515 // checkmarks displayed. |
| 516 if (track && track->mode() == TextTrack::showingKeyword()) |
| 517 trackItemInput->setChecked(true); |
| 518 } |
| 519 |
| 520 trackItem->appendChild(trackItemInput); |
| 521 String trackLabel = getTextTrackLabel(track); |
| 522 trackItem->appendChild(Text::create(document(), trackLabel)); |
| 523 // Add a track kind marker icon if there are multiple tracks with the same l
abel or if the track has no label. |
| 524 if (track && (track->label().isEmpty() || hasDuplicateLabel(track))) { |
| 525 HTMLSpanElement* trackKindMarker = HTMLSpanElement::create(document()); |
| 526 if (track->kind() == track->captionsKeyword()) { |
| 527 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con
trols-text-track-list-kind-captions")); |
| 528 } else { |
| 529 ASSERT(track->kind() == track->subtitlesKeyword()); |
| 530 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con
trols-text-track-list-kind-subtitles")); |
| 531 } |
| 532 trackItem->appendChild(trackKindMarker); |
| 533 } |
| 534 return trackItem; |
| 535 } |
| 536 |
| 537 void MediaControlTextTrackListElement::refreshTextTrackListMenu() |
| 538 { |
| 539 if (!mediaElement().hasClosedCaptions() || !mediaElement().textTracksAreRead
y()) |
| 540 return; |
| 541 |
| 542 EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents; |
| 543 removeChildren(OmitSubtreeModifiedEvent); |
| 544 |
| 545 // Construct a menu for subtitles and captions |
| 546 // Pass in a nullptr to createTextTrackListItem to create the "Off" track it
em. |
| 547 appendChild(createTextTrackListItem(nullptr)); |
| 548 |
| 549 TextTrackList* trackList = mediaElement().textTracks(); |
| 550 for (unsigned i = 0; i < trackList->length(); i++) { |
| 551 TextTrack* track = trackList->anonymousIndexedGetter(i); |
| 552 if (!track->canBeRendered()) |
| 553 continue; |
| 554 appendChild(createTextTrackListItem(track)); |
| 555 } |
| 556 } |
| 557 |
| 558 // ---------------------------- |
| 559 |
397 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon
trols) | 560 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon
trols) |
398 : MediaControlInputElement(mediaControls, MediaSlider) | 561 : MediaControlInputElement(mediaControls, MediaSlider) |
399 { | 562 { |
400 } | 563 } |
401 | 564 |
402 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls&
mediaControls) | 565 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls&
mediaControls) |
403 { | 566 { |
404 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi
aControls); | 567 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi
aControls); |
405 timeline->ensureUserAgentShadowRoot(); | 568 timeline->ensureUserAgentShadowRoot(); |
406 timeline->setType(InputTypeNames::range); | 569 timeline->setType(InputTypeNames::range); |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
691 } | 854 } |
692 | 855 |
693 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr
eate(MediaControls& mediaControls) | 856 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr
eate(MediaControls& mediaControls) |
694 { | 857 { |
695 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime
DisplayElement(mediaControls); | 858 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime
DisplayElement(mediaControls); |
696 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time
-display")); | 859 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time
-display")); |
697 return element; | 860 return element; |
698 } | 861 } |
699 | 862 |
700 } // namespace blink | 863 } // namespace blink |
OLD | NEW |