Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp

Issue 1079323002: Support text track selection in video controls (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and Philip's comments Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg leClosedCaptionsButtonElement(mediaControls); 380 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg leClosedCaptionsButtonElement(mediaControls);
365 button->ensureUserAgentShadowRoot(); 381 button->ensureUserAgentShadowRoot();
366 button->setType(InputTypeNames::button); 382 button->setType(InputTypeNames::button);
367 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed -captions-button")); 383 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed -captions-button"));
368 button->setIsWanted(false); 384 button->setIsWanted(false);
369 return button; 385 return button;
370 } 386 }
371 387
372 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() 388 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
373 { 389 {
374 bool captionsVisible = mediaElement().closedCaptionsVisible(); 390 bool captionsVisible = mediaElement().textTracksVisible();
375 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl osedCaptionsButton); 391 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl osedCaptionsButton);
376 setChecked(captionsVisible);
377 } 392 }
378 393
379 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e vent) 394 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e vent)
380 { 395 {
381 if (event->type() == EventTypeNames::click) { 396 if (event->type() == EventTypeNames::click) {
382 if (mediaElement().closedCaptionsVisible()) 397 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(); 398 updateDisplayType();
389 event->setDefaultHandled(); 399 event->setDefaultHandled();
390 } 400 }
391 401
392 HTMLInputElement::defaultEventHandler(event); 402 HTMLInputElement::defaultEventHandler(event);
393 } 403 }
394 404
395 // ---------------------------- 405 // ----------------------------
396 406
407 MediaControlTextTrackListElement::MediaControlTextTrackListElement(MediaControls & mediaControls)
408 : MediaControlDivElement(mediaControls, MediaTextTrackList)
409 {
410 }
411
412 MediaControlTextTrackListElement* MediaControlTextTrackListElement::create(Media Controls& mediaControls)
413 {
414 MediaControlTextTrackListElement* element = new MediaControlTextTrackListEle ment(mediaControls);
415 element->setShadowPseudoId(AtomicString("-internal-media-controls-text-track -list"));
416 element->setIsWanted(false);
417 return element;
418 }
419
420 void MediaControlTextTrackListElement::defaultEventHandler(Event* event)
421 {
422 if (event->type() == EventTypeNames::change) {
423 // Identify which input element was selected and set track to showing
424 Node* target = event->target()->toNode();
425 if (!target || !target->isElementNode())
426 return;
427
428 disableShowingTextTracks();
429 int trackIndex = toElement(target)->getIntegralAttribute(trackIndexAttrN ame());
430 if (trackIndex != trackIndexOffValue) {
431 ASSERT(trackIndex >= 0);
432 showTextTrackAtIndex(trackIndex);
433 mediaElement().disableAutomaticTextTrackSelection();
434 }
435
436 mediaControls().toggleTextTrackList();
437 event->setDefaultHandled();
438 }
439 MediaControlDivElement::defaultEventHandler(event);
440 }
441
442 void MediaControlTextTrackListElement::setVisible(bool visible)
443 {
444 if (visible) {
445 setIsWanted(true);
446 refreshTextTrackListMenu();
447 } else {
448 setIsWanted(false);
449 }
450 }
451
452 void MediaControlTextTrackListElement::showTextTrackAtIndex(unsigned indexToEnab le)
453 {
454 TextTrackList* trackList = mediaElement().textTracks();
455 if (indexToEnable >= trackList->length())
456 return;
457 TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable);
458 if (track && track->canBeRendered())
459 track->setMode(TextTrack::showingKeyword());
460 }
461
462 void MediaControlTextTrackListElement::disableShowingTextTracks()
463 {
464 TextTrackList* trackList = mediaElement().textTracks();
465 for (unsigned i = 0; i < trackList->length(); ++i) {
466 TextTrack* track = trackList->anonymousIndexedGetter(i);
467 if (track->mode() == TextTrack::showingKeyword())
468 track->setMode(TextTrack::disabledKeyword());
469 }
470 }
471
472 String MediaControlTextTrackListElement::getTextTrackLabel(TextTrack* track)
473 {
474 if (!track)
475 return mediaElement().locale().queryString(WebLocalizedString::TextTrack sOff);
476
477 String trackLabel = track->label();
478
479 if (trackLabel.isEmpty())
480 trackLabel = String(mediaElement().locale().queryString(WebLocalizedStri ng::TextTracksNoLabel));
481
482 return trackLabel;
483 }
484
485 bool MediaControlTextTrackListElement::hasDuplicateLabel(const TextTrack& curren tTrack)
486 {
487 TextTrackList* trackList = currentTrack.trackList();
488 // The runtime of this method is quadratic but since there are very few text tracks it won't
489 // affect the performance much.
490 for (unsigned i = 0; i < trackList->length(); i++) {
491 TextTrack* track = trackList->anonymousIndexedGetter(i);
philipj_slow 2016/04/20 15:27:22 Oh, now I see why TextTrack::trackList() wasn't co
492 if (&currentTrack != track && currentTrack.label() == track->label())
philipj_slow 2016/04/20 15:27:22 Can you extract currentTrack.label() into a variab
493 return true;
494 }
495 return false;
496 }
497
498 // TextTrack parameter when passed in as a nullptr, creates the "Off" list item in the track list.
499 Element* MediaControlTextTrackListElement::createTextTrackListItem(TextTrack* tr ack)
500 {
501 int trackIndex = track ? track->trackIndex() : trackIndexOffValue;
502 HTMLLabelElement* trackItem = HTMLLabelElement::create(document(), nullptr);
503 trackItem->setShadowPseudoId(AtomicString("-internal-media-controls-text-tra ck-list-item"));
504 HTMLInputElement* trackItemInput = HTMLInputElement::create(document(), null ptr, false);
505 trackItemInput->setShadowPseudoId(AtomicString("-internal-media-controls-tex t-track-list-item-input"));
506 trackItemInput->setType(InputTypeNames::checkbox);
507 trackItemInput->setIntegralAttribute(trackIndexAttrName(), trackIndex);
508 if (!mediaElement().textTracksVisible()) {
509 if (!track)
510 trackItemInput->setChecked(true);
511 } else {
512 // If there are multiple text tracks set to showing, they must all have
513 // checkmarks displayed.
514 if (track && track->mode() == TextTrack::showingKeyword())
515 trackItemInput->setChecked(true);
516 }
517
518 trackItem->appendChild(trackItemInput);
519 String trackLabel = getTextTrackLabel(track);
520 trackItem->appendChild(Text::create(document(), trackLabel));
521 // Add a track kind marker icon if there are multiple tracks with the same l abel.
522 if (track && (hasDuplicateLabel(*track) || track->label().isEmpty())) {
philipj_slow 2016/04/20 15:27:22 Put the isEmpty() check first to avoid the loop in
523 HTMLSpanElement* trackKindMarker = HTMLSpanElement::create(document());
524 if (track->kind() == track->captionsKeyword()) {
525 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con trols-text-track-list-kind-captions"));
526 } else {
527 ASSERT(track->kind() == track->subtitlesKeyword());
528 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con trols-text-track-list-kind-subtitles"));
529 }
530 trackItem->appendChild(trackKindMarker);
531 }
532 return trackItem;
533 }
534
535 void MediaControlTextTrackListElement::refreshTextTrackListMenu()
536 {
537 if (!mediaElement().hasClosedCaptions() || !mediaElement().textTracksAreRead y())
538 return;
539
540 EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents;
541 removeChildren(OmitSubtreeModifiedEvent);
542
543 // Construct a menu for subtitles and captions
544 // Pass in a nullptr to createTextTrackListItem to create the "Off" track it em.
545 appendChild(createTextTrackListItem(nullptr));
546
547 TextTrackList* trackList = mediaElement().textTracks();
548 for (unsigned i = 0; i < trackList->length(); i++) {
549 TextTrack* track = trackList->anonymousIndexedGetter(i);
550 if (!track->canBeRendered())
551 continue;
552 appendChild(createTextTrackListItem(track));
553 }
554 }
555
556 // ----------------------------
557
397 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon trols) 558 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon trols)
398 : MediaControlInputElement(mediaControls, MediaSlider) 559 : MediaControlInputElement(mediaControls, MediaSlider)
399 { 560 {
400 } 561 }
401 562
402 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls& mediaControls) 563 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls& mediaControls)
403 { 564 {
404 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi aControls); 565 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi aControls);
405 timeline->ensureUserAgentShadowRoot(); 566 timeline->ensureUserAgentShadowRoot();
406 timeline->setType(InputTypeNames::range); 567 timeline->setType(InputTypeNames::range);
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after
691 } 852 }
692 853
693 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr eate(MediaControls& mediaControls) 854 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr eate(MediaControls& mediaControls)
694 { 855 {
695 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime DisplayElement(mediaControls); 856 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime DisplayElement(mediaControls);
696 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time -display")); 857 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time -display"));
697 return element; 858 return element;
698 } 859 }
699 860
700 } // namespace blink 861 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698