Support list of tracks in caption media controls
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Nov 2012 06:23:27 +0000 (06:23 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Nov 2012 06:23:27 +0000 (06:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=101669

Reviewed by Eric Carlson.

Attempt two of commit. The first time caused a build failure on Chromium.

Add some new elements to the media control shadow DOM that display the list of available
tracks on an audio/video element. The UI is hidden by default everywhere but on Mac,
where it is given a very basic design. At the moment only the list of available tracks
are displayed; The followup bug will make the UI active: https://bugs.webkit.org/show_bug.cgi?id=101670

No new tests - this doesn't expose any testable surface.

* css/mediaControls.css: Added default rules that hide the new elements.
* css/mediaControlsQuickTime.css: Specific rules that give a basic rendering of the new track list.
* html/shadow/MediaControlElements.cpp:
(WebCore::MediaControlElement::isShowing): Tests for the visibility of a control.
(WebCore::MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement):
(WebCore::MediaControlClosedCaptionsContainerElement::create):
(WebCore::MediaControlClosedCaptionsContainerElement::shadowPseudoId):
(WebCore::MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement):
(WebCore::MediaControlToggleClosedCaptionsButtonElement::create): Now takes a reference to the media controls as a parameter.
(WebCore::MediaControlToggleClosedCaptionsButtonElement::updateDisplayType):
(WebCore::MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler):
(WebCore::MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId):
(WebCore::MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement): New element for holding a list of tracks to display.
* html/shadow/MediaControlElements.h:
(MediaControlElement):
(MediaControlToggleClosedCaptionsButtonElement):
(MediaControlClosedCaptionsContainerElement):
(MediaControlClosedCaptionsTrackListElement): Examines the media element to build a shadow DOM that lists all the tracks available.
* html/shadow/MediaControlRootElement.cpp:
(WebCore::MediaControlRootElement::MediaControlRootElement):
(WebCore::MediaControlRootElement::create): New track container and list elements created.
(WebCore::MediaControlRootElement::setMediaController): Hook up the new elements to the controller..
(WebCore::MediaControlRootElement::hide):
(WebCore::MediaControlRootElement::makeTransparent):
(WebCore::MediaControlRootElement::reset):
(WebCore::MediaControlRootElement::reportedError):
(WebCore::MediaControlRootElement::toggleClosedCaptionTrackList): Shows or hides the popup with the list of tracks.
(WebCore):
* html/shadow/MediaControlRootElement.h:
(WebCore):
(MediaControlRootElement):
* html/shadow/MediaControlRootElementChromium.cpp:
(WebCore::MediaControlRootElementChromium::initializeControls): Pass in the controls as a parameter.
* html/shadow/MediaControls.h:
(MediaControls):
* platform/Language.cpp:
(WebCore::displayNameForLanguageLocale): New function to return a human-readable name for a locale, given the identifier input.
* platform/Language.h:
(WebCore):
* rendering/RenderMediaControls.cpp:
(WebCore::RenderMediaControls::paintMediaControlsPart): New enum values into switch.
* rendering/RenderMediaControlsChromium.cpp:
(WebCore::RenderMediaControlsChromium::paintMediaControlsPart): New enum values into switch.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@134152 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/css/mediaControls.css
Source/WebCore/css/mediaControlsQuickTime.css
Source/WebCore/html/shadow/MediaControlElements.cpp
Source/WebCore/html/shadow/MediaControlElements.h
Source/WebCore/html/shadow/MediaControlRootElement.cpp
Source/WebCore/html/shadow/MediaControlRootElement.h
Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp
Source/WebCore/html/shadow/MediaControls.h
Source/WebCore/platform/Language.cpp
Source/WebCore/platform/Language.h
Source/WebCore/rendering/RenderMediaControls.cpp
Source/WebCore/rendering/RenderMediaControlsChromium.cpp

index 6afae29..18b8cb9 100644 (file)
@@ -1,3 +1,63 @@
+2012-11-09  Dean Jackson  <dino@apple.com>
+
+        Support list of tracks in caption media controls
+        https://bugs.webkit.org/show_bug.cgi?id=101669
+
+        Reviewed by Eric Carlson.
+
+        Attempt two of commit. The first time caused a build failure on Chromium.
+
+        Add some new elements to the media control shadow DOM that display the list of available
+        tracks on an audio/video element. The UI is hidden by default everywhere but on Mac,
+        where it is given a very basic design. At the moment only the list of available tracks
+        are displayed; The followup bug will make the UI active: https://bugs.webkit.org/show_bug.cgi?id=101670
+
+        No new tests - this doesn't expose any testable surface.
+
+        * css/mediaControls.css: Added default rules that hide the new elements.
+        * css/mediaControlsQuickTime.css: Specific rules that give a basic rendering of the new track list.
+        * html/shadow/MediaControlElements.cpp:
+        (WebCore::MediaControlElement::isShowing): Tests for the visibility of a control.
+        (WebCore::MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement):
+        (WebCore::MediaControlClosedCaptionsContainerElement::create):
+        (WebCore::MediaControlClosedCaptionsContainerElement::shadowPseudoId):
+        (WebCore::MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement):
+        (WebCore::MediaControlToggleClosedCaptionsButtonElement::create): Now takes a reference to the media controls as a parameter.
+        (WebCore::MediaControlToggleClosedCaptionsButtonElement::updateDisplayType):
+        (WebCore::MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler):
+        (WebCore::MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId):
+        (WebCore::MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement): New element for holding a list of tracks to display.
+        * html/shadow/MediaControlElements.h:
+        (MediaControlElement):
+        (MediaControlToggleClosedCaptionsButtonElement):
+        (MediaControlClosedCaptionsContainerElement):
+        (MediaControlClosedCaptionsTrackListElement): Examines the media element to build a shadow DOM that lists all the tracks available.
+        * html/shadow/MediaControlRootElement.cpp:
+        (WebCore::MediaControlRootElement::MediaControlRootElement):
+        (WebCore::MediaControlRootElement::create): New track container and list elements created.
+        (WebCore::MediaControlRootElement::setMediaController): Hook up the new elements to the controller..
+        (WebCore::MediaControlRootElement::hide):
+        (WebCore::MediaControlRootElement::makeTransparent):
+        (WebCore::MediaControlRootElement::reset):
+        (WebCore::MediaControlRootElement::reportedError):
+        (WebCore::MediaControlRootElement::toggleClosedCaptionTrackList): Shows or hides the popup with the list of tracks.
+        (WebCore):
+        * html/shadow/MediaControlRootElement.h:
+        (WebCore):
+        (MediaControlRootElement):
+        * html/shadow/MediaControlRootElementChromium.cpp:
+        (WebCore::MediaControlRootElementChromium::initializeControls): Pass in the controls as a parameter.
+        * html/shadow/MediaControls.h:
+        (MediaControls):
+        * platform/Language.cpp:
+        (WebCore::displayNameForLanguageLocale): New function to return a human-readable name for a locale, given the identifier input.
+        * platform/Language.h:
+        (WebCore):
+        * rendering/RenderMediaControls.cpp:
+        (WebCore::RenderMediaControls::paintMediaControlsPart): New enum values into switch.
+        * rendering/RenderMediaControlsChromium.cpp:
+        (WebCore::RenderMediaControlsChromium::paintMediaControlsPart): New enum values into switch.
+
 2012-11-09  Ojan Vafai  <ojan@chromium.org>
 
         Should only fire a single set of mouse events and update hover state once when scrolling is done
index 6df0340..00efcbb 100644 (file)
@@ -187,6 +187,14 @@ audio::-webkit-media-controls-toggle-closed-captions-button, video::-webkit-medi
     color: inherit;
 }
 
+audio::-webkit-media-controls-closed-captions-container, video::-webkit-media-controls-closed-captions-container {
+    display: none;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list, video::-webkit-media-controls-closed-captions-track-list {
+    display: none;
+}
+
 audio::-webkit-media-controls-volume-slider-mute-button, video::-webkit-media-controls-volume-slider-mute-button {
     -webkit-appearance: media-volume-slider-mute-button;
     display: none;
index 2eb908b..0075e2c 100644 (file)
@@ -188,17 +188,6 @@ audio::-webkit-media-controls-status-display, video::-webkit-media-controls-stat
     text-decoration: none;
 }
 
-audio::-webkit-media-controls-toggle-closed-captions-button, video::-webkit-media-controls-toggle-closed-captions-button {
-    -webkit-appearance: media-toggle-closed-captions-button;
-    display: -webkit-box;
-    width: 16px;
-    height: 16px;
-    margin-left: 7px;
-    margin-right: 7px;
-    -webkit-box-ordinal-group: 3; /* between mute and fullscreen */
-    border: none !important;
-}
-
 audio::-webkit-media-controls-volume-slider-container, video::-webkit-media-controls-volume-slider-container {
     -webkit-appearance: media-volume-slider-container;
     position: absolute;
@@ -239,3 +228,104 @@ audio::-webkit-media-controls-volume-slider-mute-button, video::-webkit-media-co
 
     border: none !important;
 }
+
+audio::-webkit-media-controls-toggle-closed-captions-button, video::-webkit-media-controls-toggle-closed-captions-button {
+    -webkit-appearance: media-toggle-closed-captions-button;
+    display: -webkit-box;
+    width: 16px;
+    height: 16px;
+    margin-left: 7px;
+    margin-right: 7px;
+    -webkit-box-ordinal-group: 3; /* between mute and fullscreen */
+    border: none !important;
+}
+
+audio::-webkit-media-controls-closed-captions-container, video::-webkit-media-controls-closed-captions-container {
+    -webkit-appearance: media-closed-captions-container;
+    position: absolute;
+    display: block;
+    bottom: 29px;
+    margin-left: -155px;
+    z-index: 2;
+    width: 200px;
+    height: 250px;
+    background-color: rgba(0, 0, 0, 0.85);
+    border: 1px solid rgb(66, 66, 66);
+    border-radius: 8px;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list, video::-webkit-media-controls-closed-captions-track-list {
+    -webkit-appearance: media-closed-captions-track-list;
+    position: absolute;
+    display: block;
+    top: 5px;
+    left: 5px;
+    width: 190px;
+    height: 240px;
+    overflow-x: hidden;
+    overflow-y: scroll;
+    text-align: left;
+    font-family: "Helvetica Bold", "Helvetica";
+    font-weight: bold;
+    font-size: 9pt;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list h3,
+video::-webkit-media-controls-closed-captions-track-list h3 {
+    margin: 0;
+    color: #bbb;
+    text-shadow: 0 1px 0 black;
+    padding-left: 4px;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    border-bottom: 1px solid #555;
+    font-size: 9pt;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list ul,
+video::-webkit-media-controls-closed-captions-track-list ul {
+    list-style-type: none;
+    margin: 0 0 8px 0;
+    padding: 0;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list li,
+video::-webkit-media-controls-closed-captions-track-list li {
+    color: white;
+    text-shadow: 0 1px 0 black;
+    margin: 0;
+    padding-left: 24px;
+    padding-right: 10px;
+    padding-top: 0.35em;
+    padding-bottom: 0.3em;
+    width: 190px;
+    height: 2.0em;
+    border-bottom: 1px solid #555;
+    box-sizing: border-box;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    -webkit-text-overflow: ellipsis;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list li:hover,
+video::-webkit-media-controls-closed-captions-track-list li:hover {
+    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgb(79, 112, 245)), color-stop(1, rgb(28, 66, 245)));
+    border-bottom: 1px solid rgb(28, 66, 245);
+}
+
+audio::-webkit-media-controls-closed-captions-track-list li.selected,
+video::-webkit-media-controls-closed-captions-track-list li.selected {
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAA9CAYAAADmgpoeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTBEQkI5QzgyMTc4MTFFMkJERDdBRjI3NEQwNzZERjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTBEQkI5QzkyMTc4MTFFMkJERDdBRjI3NEQwNzZERjAiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFMERCQjlDNjIxNzgxMUUyQkREN0FGMjc0RDA3NkRGMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFMERCQjlDNzIxNzgxMUUyQkREN0FGMjc0RDA3NkRGMCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqpWz0UAAAHPSURBVHja7JqBbYQwDEXdDRghI7BBGSEjZARGyAaMwAg3AtcJMkJug4xAUymqTggVO1yJbZ0lCyGd0D3Z/Pw4fICAWNfV5IvN+Zmzz2k2P7mX61fOx9M9X6Ccy0qLOWfHGcqt9Ji4V6oGyr2hGkD1GqG6nFEVVAGbNEIN6qAKWKCsU1KgRgLUIgXqRzASEiqydhQbME+oVi8FyhCqNYKUKGZVz3v1VC1MpLJlUVetUWO1AkgKgnUaJEFh161ZWrW8OsEoYJhqeWlQDlmtThpY1FitQWu1bhqVELsgi1NCr65aBNEYrmibefNnQrFBpuJ5tum2pFgdjOOeKMqFFA33n1CUQWXAwJXnHko8B1dAgkNOn/wVLzkVbnnBrNBcpWBUOH9i7bpxHontjsWQm0nLeSaxO8hEiFFsubCGmpZEnnH5lmCU8fNvSyLbsK0vJB7vLMhK8xiAEsXEN3MalXDUby3aOI1Kc5xeAMZve1L5mYKMo6CTLRmBa5xsSd4HDMQzY1kzjYqWFHMoTm1JB1KCuHCLG4QGdvuuC72kBYlx4OYTSI2D7c0MkuOPtc2C9NgRkgQaYkdIJtASm5F2rwnMsHfyJ+V/0gjWcWjDbwEGAN9/NX8H7V/FAAAAAElFTkSuQmCC');
+    background-repeat: no-repeat;
+    background-position: 0.5em 0.5em;
+    background-size: 1em 1em;
+}
+
+audio::-webkit-media-controls-closed-captions-track-list li.selected:hover,
+video::-webkit-media-controls-closed-captions-track-list li.selected:hover {
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAA9CAYAAADmgpoeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTBEQkI5QzgyMTc4MTFFMkJERDdBRjI3NEQwNzZERjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTBEQkI5QzkyMTc4MTFFMkJERDdBRjI3NEQwNzZERjAiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFMERCQjlDNjIxNzgxMUUyQkREN0FGMjc0RDA3NkRGMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFMERCQjlDNzIxNzgxMUUyQkREN0FGMjc0RDA3NkRGMCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqpWz0UAAAHPSURBVHja7JqBbYQwDEXdDRghI7BBGSEjZARGyAaMwAg3AtcJMkJug4xAUymqTggVO1yJbZ0lCyGd0D3Z/Pw4fICAWNfV5IvN+Zmzz2k2P7mX61fOx9M9X6Ccy0qLOWfHGcqt9Ji4V6oGyr2hGkD1GqG6nFEVVAGbNEIN6qAKWKCsU1KgRgLUIgXqRzASEiqydhQbME+oVi8FyhCqNYKUKGZVz3v1VC1MpLJlUVetUWO1AkgKgnUaJEFh161ZWrW8OsEoYJhqeWlQDlmtThpY1FitQWu1bhqVELsgi1NCr65aBNEYrmibefNnQrFBpuJ5tum2pFgdjOOeKMqFFA33n1CUQWXAwJXnHko8B1dAgkNOn/wVLzkVbnnBrNBcpWBUOH9i7bpxHontjsWQm0nLeSaxO8hEiFFsubCGmpZEnnH5lmCU8fNvSyLbsK0vJB7vLMhK8xiAEsXEN3MalXDUby3aOI1Kc5xeAMZve1L5mYKMo6CTLRmBa5xsSd4HDMQzY1kzjYqWFHMoTm1JB1KCuHCLG4QGdvuuC72kBYlx4OYTSI2D7c0MkuOPtc2C9NgRkgQaYkdIJtASm5F2rwnMsHfyJ+V/0gjWcWjDbwEGAN9/NX8H7V/FAAAAAElFTkSuQmCC'), -webkit-gradient(linear, left top, left bottom, color-stop(0, rgb(79, 112, 245)), color-stop(1, rgb(28, 66, 245)));
+    background-repeat: no-repeat, repeat;
+    background-position: 0.5em 0.5em, top left;
+    background-size: 1em 1em, 100% 100%;
+}
index 0bbf763..a71b36c 100644 (file)
@@ -43,6 +43,7 @@
 #include "HTMLMediaElement.h"
 #include "HTMLNames.h"
 #include "HTMLVideoElement.h"
+#include "Language.h"
 #include "LayoutRepainter.h"
 #include "LocalizedStrings.h"
 #include "MediaControlRootElement.h"
@@ -62,6 +63,9 @@
 #include "Settings.h"
 #include "StyleResolver.h"
 #include "Text.h"
+#if ENABLE(VIDEO_TRACK)
+#include "TextTrackList.h"
+#endif
 
 namespace WebCore {
 
@@ -114,6 +118,14 @@ void MediaControlElement::hide()
     setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
 }
 
+bool MediaControlElement::isShowing() const
+{
+    const StylePropertySet* propertySet = inlineStyle();
+    // Following the code from show() and hide() above, we only have
+    // to check for the presense of inline display.
+    return (!propertySet || !propertySet->getPropertyCSSValue(CSSPropertyDisplay));
+}
+
 // ----------------------------
 
 inline MediaControlPanelElement::MediaControlPanelElement(Document* document)
@@ -851,25 +863,56 @@ const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId()
 
 // ----------------------------
 
-inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document)
+inline MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement(Document* document)
+    : MediaControlElement(document)
+{
+}
+
+PassRefPtr<MediaControlClosedCaptionsContainerElement> MediaControlClosedCaptionsContainerElement::create(Document* document)
+{
+    RefPtr<MediaControlClosedCaptionsContainerElement> element = adoptRef(new MediaControlClosedCaptionsContainerElement(document));
+    element->hide();
+    return element.release();
+}
+
+const AtomicString& MediaControlClosedCaptionsContainerElement::shadowPseudoId() const
+{
+    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-container", AtomicString::ConstructFromLiteral));
+    return id;
+}
+
+// ----------------------------
+
+inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document, MediaControls* controls)
     : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
+    , m_controls(controls)
 {
 }
 
-PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document)
+PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document, MediaControls* controls)
 {
-    RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document));
+    ASSERT(controls);
+
+    RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls));
     button->createShadowSubtree();
     button->setType("button");
     button->hide();
     return button.release();
 }
 
+void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
+{
+    setDisplayType(mediaController()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
+}
+
 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
 {
     if (event->type() == eventNames().clickEvent) {
+        // FIXME: This is now incorrectly doing two things at once: showing the list of captions and toggling display.
+        // https://bugs.webkit.org/show_bug.cgi?id=101670
         mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
         setChecked(mediaController()->closedCaptionsVisible());
+        m_controls->toggleClosedCaptionTrackList();
         updateDisplayType();
         event->setDefaultHandled();
     }
@@ -877,17 +920,117 @@ void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e
     HTMLInputElement::defaultEventHandler(event);
 }
 
-void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
+const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
 {
-    setDisplayType(mediaController()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
+    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
+    return id;
 }
 
-const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
+// ----------------------------
+
+inline MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document* document)
+    : MediaControlElement(document)
 {
-    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
+}
+
+PassRefPtr<MediaControlClosedCaptionsTrackListElement> MediaControlClosedCaptionsTrackListElement::create(Document* document)
+{
+    RefPtr<MediaControlClosedCaptionsTrackListElement> element = adoptRef(new MediaControlClosedCaptionsTrackListElement(document));
+    return element.release();
+}
+
+void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* event)
+{
+    // FIXME: Hook this up to actual text tracks.
+    // https://bugs.webkit.org/show_bug.cgi?id=101670
+    UNUSED_PARAM(event);
+}
+
+const AtomicString& MediaControlClosedCaptionsTrackListElement::shadowPseudoId() const
+{
+    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-track-list", AtomicString::ConstructFromLiteral));
     return id;
 }
 
+void MediaControlClosedCaptionsTrackListElement::updateDisplay()
+{
+#if ENABLE(VIDEO_TRACK)
+    // Remove any existing content.
+    removeChildren();
+
+    if (!mediaController()->hasClosedCaptions())
+        return;
+
+    HTMLMediaElement* mediaElement = toParentMediaElement(this);
+    if (!mediaElement)
+        return;
+
+    TextTrackList* trackList = mediaElement->textTracks();
+
+    if (!trackList || !trackList->length())
+        return;
+
+    Document* doc = document();
+
+    RefPtr<Element> captionsSection = doc->createElement(sectionTag, ASSERT_NO_EXCEPTION);
+    RefPtr<Element> captionsHeader = doc->createElement(h3Tag, ASSERT_NO_EXCEPTION);
+    captionsHeader->appendChild(doc->createTextNode("Closed Captions"));
+    captionsSection->appendChild(captionsHeader);
+    RefPtr<Element> captionsList = doc->createElement(ulTag, ASSERT_NO_EXCEPTION);
+
+    RefPtr<Element> subtitlesSection = doc->createElement(sectionTag, ASSERT_NO_EXCEPTION);
+    RefPtr<Element> subtitlesHeader = doc->createElement(h3Tag, ASSERT_NO_EXCEPTION);
+    subtitlesHeader->appendChild(doc->createTextNode("Subtitles"));
+    subtitlesSection->appendChild(subtitlesHeader);
+    RefPtr<Element> subtitlesList = doc->createElement(ulTag, ASSERT_NO_EXCEPTION);
+
+    RefPtr<Element> trackItem;
+
+    trackItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+    trackItem->appendChild(doc->createTextNode("Off"));
+    // FIXME: These lists are not yet live. Mark the Off entry as the selected one for now.
+    trackItem->setAttribute(classAttr, "selected");
+    captionsList->appendChild(trackItem);
+
+    trackItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+    trackItem->appendChild(doc->createTextNode("Off"));
+    // FIXME: These lists are not yet live. Mark the Off entry as the selected one for now.
+    trackItem->setAttribute(classAttr, "selected");
+    subtitlesList->appendChild(trackItem);
+
+    bool hasCaptions = false;
+    bool hasSubtitles = false;
+
+    for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
+        TextTrack* track = trackList->item(i);
+        trackItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+        AtomicString labelText = track->label();
+        if (labelText.isNull() || labelText.isEmpty())
+            labelText = displayNameForLanguageLocale(track->language());
+        if (labelText.isNull() || labelText.isEmpty())
+            labelText = "No Label";
+
+        if (track->kind() == track->captionsKeyword()) {
+            hasCaptions = true;
+            captionsList->appendChild(trackItem);
+        }
+        if (track->kind() == track->subtitlesKeyword()) {
+            hasSubtitles = true;
+            subtitlesList->appendChild(trackItem);
+        }
+        trackItem->appendChild(doc->createTextNode(labelText));
+    }
+
+    captionsSection->appendChild(captionsList);
+    subtitlesSection->appendChild(subtitlesList);
+
+    if (hasCaptions)
+        appendChild(captionsSection);
+    if (hasSubtitles)
+        appendChild(subtitlesSection);
+#endif
+}
+
 // ----------------------------
 
 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls)
index 66cb2da..c3fbc81 100644 (file)
@@ -75,6 +75,8 @@ enum MediaControlElementType {
     MediaTextTrackDisplay,
     MediaExitFullscreenButton,
     MediaOverlayPlayButton,
+    MediaClosedCaptionsContainer,
+    MediaClosedCaptionsTrackList,
 };
 
 HTMLMediaElement* toParentMediaElement(Node*);
@@ -88,6 +90,7 @@ class MediaControlElement : public HTMLDivElement {
 public:
     void hide();
     void show();
+    bool isShowing() const;
 
     virtual MediaControlElementType displayType() const = 0;
 
@@ -380,17 +383,51 @@ private:
 
 class MediaControlToggleClosedCaptionsButtonElement : public MediaControlInputElement {
 public:
-    static PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> create(Document*);
+    static PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> create(Document*, MediaControls*);
 
-    virtual void defaultEventHandler(Event*);
     virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+    virtual void defaultEventHandler(Event*);
     virtual void updateDisplayType();
 
 private:
-    MediaControlToggleClosedCaptionsButtonElement(Document*);
+    MediaControlToggleClosedCaptionsButtonElement(Document*, MediaControls*);
+    virtual const AtomicString& shadowPseudoId() const;
+
+    MediaControls* m_controls;
+};
+
+// ----------------------------
 
+class MediaControlClosedCaptionsContainerElement : public MediaControlElement {
+public:
+    static PassRefPtr<MediaControlClosedCaptionsContainerElement> create(Document*);
+
+    virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+private:
+    MediaControlClosedCaptionsContainerElement(Document*);
+
+    virtual MediaControlElementType displayType() const { return MediaClosedCaptionsContainer; }
     virtual const AtomicString& shadowPseudoId() const;
-};    
+};
+
+// ----------------------------
+
+class MediaControlClosedCaptionsTrackListElement : public MediaControlElement {
+public:
+    static PassRefPtr<MediaControlClosedCaptionsTrackListElement> create(Document*);
+
+    virtual void defaultEventHandler(Event*);
+    virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+    void updateDisplay();
+
+private:
+    MediaControlClosedCaptionsTrackListElement(Document*);
+
+    virtual MediaControlElementType displayType() const { return MediaClosedCaptionsTrackList; }
+    virtual const AtomicString& shadowPseudoId() const;
+};
 
 // ----------------------------
 
index 22a018f..6d7c108 100644 (file)
@@ -62,6 +62,8 @@ MediaControlRootElement::MediaControlRootElement(Document* document)
     , m_seekBackButton(0)
     , m_seekForwardButton(0)
     , m_toggleClosedCaptionsButton(0)
+    , m_closedCaptionsTrackList(0)
+    , m_closedCaptionsContainer(0)
     , m_panelMuteButton(0)
     , m_volumeSlider(0)
     , m_volumeSliderMuteButton(0)
@@ -162,11 +164,25 @@ PassRefPtr<MediaControlRootElement> MediaControlRootElement::create(Document* do
         return 0;
 
     if (document->page()->theme()->supportsClosedCaptioning()) {
-        RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document);
+
+        RefPtr<MediaControlClosedCaptionsContainerElement> closedCaptionsContainer = MediaControlClosedCaptionsContainerElement::create(document);
+
+        RefPtr<MediaControlClosedCaptionsTrackListElement> closedCaptionsTrackList = MediaControlClosedCaptionsTrackListElement::create(document);
+        controls->m_closedCaptionsTrackList = closedCaptionsTrackList.get();
+        closedCaptionsContainer->appendChild(closedCaptionsTrackList.release(), ec, true);
+        if (ec)
+            return 0;
+
+        RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, controls.get());
         controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
         panel->appendChild(toggleClosedCaptionsButton.release(), ec, true);
         if (ec)
             return 0;
+
+        controls->m_closedCaptionsContainer = closedCaptionsContainer.get();
+        panel->appendChild(closedCaptionsContainer.release(), ec, true);
+        if (ec)
+            return 0;
     }
 
     // FIXME: Only create when needed <http://webkit.org/b/57163>
@@ -266,6 +282,10 @@ void MediaControlRootElement::setMediaController(MediaControllerInterface* contr
         m_seekForwardButton->setMediaController(controller);
     if (m_toggleClosedCaptionsButton)
         m_toggleClosedCaptionsButton->setMediaController(controller);
+    if (m_closedCaptionsTrackList)
+        m_closedCaptionsTrackList->setMediaController(controller);
+    if (m_closedCaptionsContainer)
+        m_closedCaptionsContainer->setMediaController(controller);
     if (m_panelMuteButton)
         m_panelMuteButton->setMediaController(controller);
     if (m_volumeSlider)
@@ -302,6 +322,7 @@ void MediaControlRootElement::hide()
     m_panel->setIsDisplayed(false);
     m_panel->hide();
     m_volumeSliderContainer->hide();
+    m_closedCaptionsContainer->hide();
 }
 
 void MediaControlRootElement::makeOpaque()
@@ -313,6 +334,7 @@ void MediaControlRootElement::makeTransparent()
 {
     m_panel->makeTransparent();
     m_volumeSliderContainer->hide();
+    m_closedCaptionsContainer->hide();
 }
 
 void MediaControlRootElement::reset()
@@ -346,9 +368,10 @@ void MediaControlRootElement::reset()
         m_volumeSlider->setVolume(m_mediaController->volume());
 
     if (m_toggleClosedCaptionsButton) {
-        if (m_mediaController->hasClosedCaptions())
+        if (m_mediaController->hasClosedCaptions()) {
             m_toggleClosedCaptionsButton->show();
-        else
+            m_closedCaptionsTrackList->updateDisplay();
+        } else
             m_toggleClosedCaptionsButton->hide();
     }
 
@@ -447,6 +470,8 @@ void MediaControlRootElement::reportedError()
         m_volumeSliderContainer->hide();
     if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
         m_toggleClosedCaptionsButton->hide();
+    if (m_closedCaptionsContainer)
+        m_closedCaptionsContainer->hide();
 }
 
 void MediaControlRootElement::updateStatusDisplay()
@@ -542,6 +567,19 @@ void MediaControlRootElement::showVolumeSlider()
         m_volumeSliderContainer->show();
 }
 
+void MediaControlRootElement::toggleClosedCaptionTrackList()
+{
+    if (!m_mediaController->hasClosedCaptions())
+        return;
+
+    if (m_closedCaptionsContainer) {
+        if (m_closedCaptionsContainer->isShowing())
+            m_closedCaptionsContainer->hide();
+        else
+            m_closedCaptionsContainer->show();
+    }
+}
+
 bool MediaControlRootElement::shouldHideControls()
 {
     return !m_panel->hovered();
index a0b09d8..06739be 100644 (file)
@@ -42,7 +42,9 @@ class MediaControlPlayButtonElement;
 class MediaControlSeekButtonElement;
 class MediaControlRewindButtonElement;
 class MediaControlReturnToRealtimeButtonElement;
+class MediaControlClosedCaptionsContainerElement;
 class MediaControlToggleClosedCaptionsButtonElement;
+class MediaControlClosedCaptionsTrackListElement;
 class MediaControlCurrentTimeDisplayElement;
 class MediaControlTimelineElement;
 class MediaControlTimeRemainingDisplayElement;
@@ -103,6 +105,8 @@ public:
     void updateTimeDisplay();
     void updateStatusDisplay();
 
+    void toggleClosedCaptionTrackList();
+
 #if ENABLE(VIDEO_TRACK)
     void createTextTrackDisplay();
     void showTextTrackDisplay();
@@ -141,6 +145,8 @@ private:
     MediaControlSeekBackButtonElement* m_seekBackButton;
     MediaControlSeekForwardButtonElement* m_seekForwardButton;
     MediaControlToggleClosedCaptionsButtonElement* m_toggleClosedCaptionsButton;
+    MediaControlClosedCaptionsTrackListElement* m_closedCaptionsTrackList;
+    MediaControlClosedCaptionsContainerElement* m_closedCaptionsContainer;
     MediaControlPanelMuteButtonElement* m_panelMuteButton;
     MediaControlVolumeSliderElement* m_volumeSlider;
     MediaControlVolumeSliderMuteButtonElement* m_volumeSliderMuteButton;
index 82d8f92..c259fdd 100644 (file)
@@ -166,7 +166,7 @@ bool MediaControlRootElementChromium::initializeControls(Document* document)
         return false;
 
     if (document->page()->theme()->supportsClosedCaptioning()) {
-        RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document);
+        RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, this);
         m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
         panel->appendChild(toggleClosedCaptionsButton.release(), ec, true);
         if (ec)
index 28908a9..6f0fad8 100644 (file)
@@ -70,6 +70,8 @@ class MediaControls : public HTMLDivElement {
     virtual void updateTimeDisplay() = 0;
     virtual void updateStatusDisplay() = 0;
 
+    virtual void toggleClosedCaptionTrackList() { }
+
     virtual bool shouldHideControls() = 0;
 
 #if ENABLE(VIDEO_TRACK)
index dba3a89..e28e76e 100644 (file)
 #include "Language.h"
 
 #include <wtf/HashMap.h>
+#include <wtf/RetainPtr.h>
 #include <wtf/text/WTFString.h>
 
+#if PLATFORM(MAC)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
 namespace WebCore {
 
 typedef HashMap<void*, LanguageChangeObserverFunction> ObserverMap;
@@ -142,5 +147,14 @@ String preferredLanguageFromList(const Vector<String>& languageList)
 
     return emptyString();
 }
-    
+
+String displayNameForLanguageLocale(const String& localeName)
+{
+#if PLATFORM(MAC)
+    if (!localeName.isNull() && !localeName.isEmpty())
+        return CFLocaleCopyDisplayNameForPropertyValue(CFLocaleCopyCurrent(), kCFLocaleIdentifier, localeName.createCFString().get());
+#endif
+    return localeName;
+}
+
 }
index 411615c..29f1ca8 100644 (file)
@@ -43,6 +43,8 @@ void removeLanguageChangeObserver(void* context);
 
 Vector<String> platformUserPreferredLanguages();
 
+String displayNameForLanguageLocale(const String&);
+
 // Called from platform specific code when the user's preferred language(s) change.
 void languageDidChange();
 }
index cfaed5e..5002be1 100644 (file)
@@ -197,6 +197,8 @@ bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, R
         ASSERT_NOT_REACHED();
     case MediaTextTrackDisplayContainer:
     case MediaTextTrackDisplay:
+    case MediaClosedCaptionsContainer:
+    case MediaClosedCaptionsTrackList:
         ASSERT_NOT_REACHED();
         break;
 }
index 38a4e5a..8b736b3 100644 (file)
@@ -383,6 +383,10 @@ bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType
     case MediaTextTrackDisplay:
     case MediaFullScreenVolumeSlider:
     case MediaFullScreenVolumeSliderThumb:
+    case MediaFullScreenVolumeSlider:
+    case MediaFullScreenVolumeSliderThumb:
+    case MediaClosedCaptionsContainer:
+    case MediaClosedCaptionsTrackList:
         ASSERT_NOT_REACHED();
         break;
     }