Render text tracks
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Dec 2011 17:25:37 +0000 (17:25 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Dec 2011 17:25:37 +0000 (17:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=62886

Reviewed by Sam Weinig.

Source/WebCore:

Test: media/track/track-cue-rendering.html

* css/mediaControls.css:
(video::-webkit-media-text-track-container):
(video::-webkit-media-text-track-display):

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement): Initialize m_haveVisibleTextTrack.
(WebCore::HTMLMediaElement::updateActiveTextTrackCues): Trigger an update of the text
    track display.
(WebCore::HTMLMediaElement::textTrackModeChanged): call configureTextTrackDisplay() so
    the text track display is hidden or shown when necessary.
(WebCore::HTMLMediaElement::userIsInterestedInThisTrack): Minor cleanup.
(WebCore::HTMLMediaElement::createMediaControls): configureMediaControls() always called
    reset after creating the controls, do it here instead.
(WebCore::HTMLMediaElement::configureMediaControls): Simplify and cleanup.
(WebCore::HTMLMediaElement::configureTextTrackDisplay): Show and hide text track display.
* html/HTMLMediaElement.h:
(WebCore::HTMLMediaElement::currentlyVisibleCues):

* html/shadow/MediaControlElements.cpp:
(WebCore::RenderTextTrackContainerElement::RenderTextTrackContainerElement): New.
(WebCore::RenderTextTrackContainerElement::layout): New. Call the display element so it can
    update the position and font size.
(WebCore::MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement): New.
(WebCore::MediaControlTextTrackContainerElement::create): Ditto.
(WebCore::MediaControlTextTrackContainerElement::createRenderer): Ditto.
(WebCore::MediaControlTextTrackContainerElement::shadowPseudoId): Ditto.
(WebCore::MediaControlTextTrackContainerElement::updateSizes): Keep the cue display element
    positioned above the bottom of the video box, and size the font according to the video height.
(WebCore::MediaControlTextTrackDisplayElement::MediaControlTextTrackDisplayElement): New.
(WebCore::MediaControlTextTrackDisplayElement::create): Ditto.
(WebCore::MediaControlTextTrackDisplayElement::shadowPseudoId): Ditto.
* html/shadow/MediaControlElements.h:
(WebCore::MediaControlTextTrackContainerElement::displayType):
(WebCore::MediaControlTextTrackDisplayElement::displayType):

* html/shadow/MediaControlRootElement.cpp:
(WebCore::MediaControlRootElement::MediaControlRootElement): New.
(WebCore::MediaControlRootElement::setMediaController): Ditto.
(WebCore::MediaControlRootElement::createTextTrackDisplay): Ditto.
(WebCore::MediaControlRootElement::showTextTrackDisplay): Ditto.
(WebCore::MediaControlRootElement::hideTextTrackDisplay): Ditto.
(WebCore::MediaControlRootElement::updateTextTrackDisplay): Ditto.
* html/shadow/MediaControlRootElement.h:

* html/shadow/MediaControlRootElementChromium.cpp:
(WebCore::MediaControlRootElementChromium::MediaControlRootElementChromium): New.
(WebCore::MediaControlRootElement::createTextTrackDisplay): Ditto.
(WebCore::MediaControlRootElement::showTextTrackDisplay): Ditto.
(WebCore::MediaControlRootElement::hideTextTrackDisplay): Ditto.
(WebCore::MediaControlRootElement::updateTextTrackDisplay): Ditto.
* html/shadow/MediaControlRootElementChromium.h:
* html/shadow/MediaControls.h:

LayoutTests:

* media/media-controls.js:
(mediaControlsElement): Don't always look for a controller element.
(mediaControlsButtonCoordinates):
(textTrackDisplayElement): Find the cue display element.

* media/track/track-cue-rendering-expected.txt: Added.
* media/track/track-cue-rendering.html: Added.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/media/media-controls.js
LayoutTests/media/track/captions-webvtt/captions.vtt [new file with mode: 0644]
LayoutTests/media/track/track-cue-rendering-expected.txt [new file with mode: 0644]
LayoutTests/media/track/track-cue-rendering.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/mediaControls.css
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
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/MediaControlRootElementChromium.h
Source/WebCore/html/shadow/MediaControls.h
Source/WebCore/rendering/RenderMediaControls.cpp
Source/WebCore/rendering/RenderMediaControlsChromium.cpp

index 2ef6f7e..c43d9d3 100644 (file)
@@ -1,3 +1,18 @@
+2011-12-19  Eric Carlson  <eric.carlson@apple.com>
+
+        Render text tracks
+        https://bugs.webkit.org/show_bug.cgi?id=62886
+
+        Reviewed by Sam Weinig.
+
+        * media/media-controls.js:
+        (mediaControlsElement): Don't always look for a controller element.
+        (mediaControlsButtonCoordinates):
+        (textTrackDisplayElement): Find the cue display element.
+
+        * media/track/track-cue-rendering-expected.txt: Added.
+        * media/track/track-cue-rendering.html: Added.
+
 2011-12-19  Csaba Osztrogonác  <ossy@webkit.org>
 
         [Qt] Test fonts are not used with Qt5
index c18e909..691d14a 100644 (file)
@@ -1,13 +1,12 @@
 
 function mediaControlsElement(first, id)
 {
-    var controlID = "-webkit-media-controls-" + id;
     for (var element = first; element; element = element.nextSibling) {
 
         // Not every element in the media controls has a shadow pseudo ID, eg. the
         // text nodes for the time values, so guard against exceptions.
         try {
-            if (internals.shadowPseudoId(element) == controlID)
+            if (internals.shadowPseudoId(element) == id)
                 return element;
         } catch (exception) { }
 
@@ -23,7 +22,8 @@ function mediaControlsElement(first, id)
 
 function mediaControlsButtonCoordinates(element, id)
 {
-    var button = mediaControlsElement(internals.shadowRoot(element).firstChild, id);
+    var controlID = "-webkit-media-controls-" + id;
+    var button = mediaControlsElement(internals.shadowRoot(element).firstChild, controlID);
     if (!button)
         throw "Failed to find media control element ID '" + id + "'";
 
@@ -32,3 +32,12 @@ function mediaControlsButtonCoordinates(element, id)
     var y = buttonBoundingRect.top + buttonBoundingRect.height / 2;
     return new Array(x, y);
 }
+
+function textTrackDisplayElement(parentElement)
+{
+    var controlID = "-webkit-media-text-track-display";
+    var displayElement = mediaControlsElement(internals.shadowRoot(parentElement).firstChild, controlID);
+    if (!displayElement)
+        throw "Failed to find media control element ID '" + controlID + "'";
+    return displayElement;
+}
\ No newline at end of file
diff --git a/LayoutTests/media/track/captions-webvtt/captions.vtt b/LayoutTests/media/track/captions-webvtt/captions.vtt
new file mode 100644 (file)
index 0000000..787c430
--- /dev/null
@@ -0,0 +1,18 @@
+WEBVTT
+
+1
+00:00:00.000 --> 00:00:01.000
+Lorem
+
+2
+00:00:01.000 --> 00:00:02.000
+ipsum
+
+3
+00:00:02.000 --> 00:00:03.000
+dolor
+
+4
+00:00:03.000 --> 00:00:04.000
+sit
+
diff --git a/LayoutTests/media/track/track-cue-rendering-expected.txt b/LayoutTests/media/track/track-cue-rendering-expected.txt
new file mode 100644 (file)
index 0000000..dee1601
--- /dev/null
@@ -0,0 +1,47 @@
+Test that TextTrack's cues are rendered correctly.
+EVENT(canplaythrough)
+EVENT(seeked)
+EXPECTED (video.currentTime == '0.5') OK
+EXPECTED (testTrack.track.activeCues.length == '1') OK
+EXPECTED (testTrack.track.activeCues[0].getCueAsSource() == 'Lorem') OK
+EXPECTED (textTrackDisplayElement(video).innerText == 'Lorem') OK
+
+RUN(video.currentTime = 1.5)
+EVENT(seeked)
+EXPECTED (video.currentTime == '1.5') OK
+EXPECTED (testTrack.track.activeCues.length == '1') OK
+EXPECTED (testTrack.track.activeCues[0].getCueAsSource() == 'ipsum') OK
+EXPECTED (textTrackDisplayElement(video).innerText == 'ipsum') OK
+
+RUN(video.currentTime = 2.5)
+EVENT(seeked)
+EXPECTED (video.currentTime == '2.5') OK
+EXPECTED (testTrack.track.activeCues.length == '1') OK
+EXPECTED (testTrack.track.activeCues[0].getCueAsSource() == 'dolor') OK
+EXPECTED (textTrackDisplayElement(video).innerText == 'dolor') OK
+
+RUN(video.currentTime = 3.5)
+EVENT(seeked)
+EXPECTED (video.currentTime == '3.5') OK
+EXPECTED (testTrack.track.activeCues.length == '1') OK
+EXPECTED (testTrack.track.activeCues[0].getCueAsSource() == 'sit') OK
+EXPECTED (textTrackDisplayElement(video).innerText == 'sit') OK
+
+Test that the cue display font size is resized as the video element resizes.
+RUN(video.width = 320)
+RUN(video.height = 240)
+EXPECTED (getComputedStyle(textTrackDisplayElement(video)).fontSize == '16px') OK
+
+RUN(video.width = 640)
+RUN(video.height = 480)
+EXPECTED (getComputedStyle(textTrackDisplayElement(video)).fontSize == '19px') OK
+
+RUN(video.width = 1280)
+RUN(video.height = 960)
+EXPECTED (getComputedStyle(textTrackDisplayElement(video)).fontSize == '38px') OK
+
+RUN(video.width = 2560)
+RUN(video.height = 1440)
+EXPECTED (getComputedStyle(textTrackDisplayElement(video)).fontSize == '58px') OK
+END OF TEST
+
diff --git a/LayoutTests/media/track/track-cue-rendering.html b/LayoutTests/media/track/track-cue-rendering.html
new file mode 100644 (file)
index 0000000..b2c6f4d
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+        <script src=../media-file.js></script>
+        <script src=../video-test.js></script>
+        <script src=../media-controls.js></script>
+
+        <script>            
+
+        var testTrack;
+        var seekedCount = 0;
+        var info = [ "Lorem", "ipsum", "dolor", "sit" ];
+
+        function testFontSizes()
+        {
+            consoleWrite("<br>Test that the cue display font size is resized as the video element resizes.");
+            run("video.width = 320");
+            run("video.height = 240");
+            document.body.offsetTop;
+            testExpected("getComputedStyle(textTrackDisplayElement(video)).fontSize", "16px");
+
+            consoleWrite("");
+            run("video.width = 640");
+            run("video.height = 480");
+            document.body.offsetTop;
+            testExpected("getComputedStyle(textTrackDisplayElement(video)).fontSize", "19px");
+
+            consoleWrite("");
+            run("video.width = 1280");
+            run("video.height = 960");
+            document.body.offsetTop;
+            testExpected("getComputedStyle(textTrackDisplayElement(video)).fontSize", "38px");
+
+            consoleWrite("");
+            run("video.width = 2560");
+            run("video.height = 1440");
+            document.body.offsetTop;
+            testExpected("getComputedStyle(textTrackDisplayElement(video)).fontSize", "58px");
+
+            endTest();
+        }
+
+        function seeked()
+        {
+            testExpected("video.currentTime", seekedCount + .5);
+            testExpected("testTrack.track.activeCues.length", 1);
+            testExpected("testTrack.track.activeCues[0].getCueAsSource()", info[seekedCount]);
+            testExpected("textTrackDisplayElement(video).innerText", info[seekedCount]);
+
+            if (++seekedCount == info.length)
+                testFontSizes();
+            else {
+                consoleWrite("");
+                run("video.currentTime = " + (video.currentTime + 1));
+                return;
+            }
+        }
+
+        function loaded()
+        {
+            consoleWrite("Test that TextTrack's cues are rendered correctly.");
+            findMediaElement();
+            testTrack = document.querySelectorAll('track')[0];
+            video.src = findMediaFile('video', '../content/test');
+            waitForEvent('seeked', seeked);
+            waitForEvent('canplaythrough', function() { video.currentTime = .5; });
+        }
+        
+        </script>
+    </head>
+    <body onload="loaded()">
+        <video controls >
+            <track src="captions-webvtt/captions.vtt" kind="captions" default>
+        </video>
+    </body>
+</html>
index 4957964..35fc77f 100644 (file)
@@ -1,5 +1,67 @@
 2011-12-19  Eric Carlson  <eric.carlson@apple.com>
 
+        Render text tracks
+        https://bugs.webkit.org/show_bug.cgi?id=62886
+
+        Reviewed by Sam Weinig.
+
+        Test: media/track/track-cue-rendering.html
+
+        * css/mediaControls.css:
+        (video::-webkit-media-text-track-container):
+        (video::-webkit-media-text-track-display):
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement): Initialize m_haveVisibleTextTrack.
+        (WebCore::HTMLMediaElement::updateActiveTextTrackCues): Trigger an update of the text
+            track display.
+        (WebCore::HTMLMediaElement::textTrackModeChanged): call configureTextTrackDisplay() so 
+            the text track display is hidden or shown when necessary.
+        (WebCore::HTMLMediaElement::userIsInterestedInThisTrack): Minor cleanup.
+        (WebCore::HTMLMediaElement::createMediaControls): configureMediaControls() always called
+            reset after creating the controls, do it here instead.
+        (WebCore::HTMLMediaElement::configureMediaControls): Simplify and cleanup.
+        (WebCore::HTMLMediaElement::configureTextTrackDisplay): Show and hide text track display.
+        * html/HTMLMediaElement.h:
+        (WebCore::HTMLMediaElement::currentlyVisibleCues):
+
+        * html/shadow/MediaControlElements.cpp:
+        (WebCore::RenderTextTrackContainerElement::RenderTextTrackContainerElement): New.
+        (WebCore::RenderTextTrackContainerElement::layout): New. Call the display element so it can
+            update the position and font size.
+        (WebCore::MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement): New.
+        (WebCore::MediaControlTextTrackContainerElement::create): Ditto.
+        (WebCore::MediaControlTextTrackContainerElement::createRenderer): Ditto.
+        (WebCore::MediaControlTextTrackContainerElement::shadowPseudoId): Ditto.
+        (WebCore::MediaControlTextTrackContainerElement::updateSizes): Keep the cue display element
+            positioned above the bottom of the video box, and size the font according to the video height.
+        (WebCore::MediaControlTextTrackDisplayElement::MediaControlTextTrackDisplayElement): New.
+        (WebCore::MediaControlTextTrackDisplayElement::create): Ditto.
+        (WebCore::MediaControlTextTrackDisplayElement::shadowPseudoId): Ditto.
+        * html/shadow/MediaControlElements.h:
+        (WebCore::MediaControlTextTrackContainerElement::displayType):
+        (WebCore::MediaControlTextTrackDisplayElement::displayType):
+
+        * html/shadow/MediaControlRootElement.cpp:
+        (WebCore::MediaControlRootElement::MediaControlRootElement): New.
+        (WebCore::MediaControlRootElement::setMediaController): Ditto.
+        (WebCore::MediaControlRootElement::createTextTrackDisplay): Ditto.
+        (WebCore::MediaControlRootElement::showTextTrackDisplay): Ditto.
+        (WebCore::MediaControlRootElement::hideTextTrackDisplay): Ditto.
+        (WebCore::MediaControlRootElement::updateTextTrackDisplay): Ditto.
+        * html/shadow/MediaControlRootElement.h:
+
+        * html/shadow/MediaControlRootElementChromium.cpp:
+        (WebCore::MediaControlRootElementChromium::MediaControlRootElementChromium): New.
+        (WebCore::MediaControlRootElement::createTextTrackDisplay): Ditto.
+        (WebCore::MediaControlRootElement::showTextTrackDisplay): Ditto.
+        (WebCore::MediaControlRootElement::hideTextTrackDisplay): Ditto.
+        (WebCore::MediaControlRootElement::updateTextTrackDisplay): Ditto.
+        * html/shadow/MediaControlRootElementChromium.h:
+        * html/shadow/MediaControls.h:
+
+2011-12-19  Eric Carlson  <eric.carlson@apple.com>
+
         Enable <track> for Mac build
         https://bugs.webkit.org/show_bug.cgi?id=74838
 
index 9f6eb6a..14e3b23 100644 (file)
@@ -186,3 +186,28 @@ audio::-webkit-media-controls-fullscreen-volume-min-button, video::-webkit-media
 audio::-webkit-media-controls-fullscreen-volume-max-button, video::-webkit-media-controls-fullscreen-volume-max-button {
     display: none;
 }
+
+video::-webkit-media-text-track-container {
+    position: absolute;
+    width: 100%;
+    overflow: hidden;
+
+    font-size: 22px;
+    font-family: sans-serif;
+    text-align: center;
+    
+    letter-spacing: normal;
+    word-spacing: normal;
+    text-transform: none;
+    text-indent: 0;
+    text-decoration: none;
+    pointer-events: none;
+    -webkit-user-select: none;
+}
+
+video::-webkit-media-text-track-display {
+    display: inline;
+    background-color: rgba(0, 0, 0, 0.5);
+    color: yellow;
+    padding: 0px 2px;
+}
index 6462aab..d1289cb 100644 (file)
@@ -231,6 +231,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
     , m_parsingInProgress(createdByParser)
 #if ENABLE(VIDEO_TRACK)
     , m_tracksAreReady(true)
+    , m_haveVisibleTextTrack(false)
     , m_textTracks(0)
 #endif
 #if ENABLE(WEB_AUDIO)
@@ -940,23 +941,31 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content
 #if ENABLE(VIDEO_TRACK)
 void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
 {
-    Vector<CueIntervalTree::IntervalType> previouslyVisibleCues = m_currentlyVisibleCues;
+    CueList previouslyActiveCues = m_currentlyActiveCues;
+    bool activeSetChanged = false;
 
-    m_currentlyVisibleCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
+    m_currentlyActiveCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
     
     // FIXME(72171): Events need to be sorted and filtered before dispatching.
 
-    for (size_t i = 0; i < previouslyVisibleCues.size(); ++i) {
-        if (!m_currentlyVisibleCues.contains(previouslyVisibleCues[i]))
-            previouslyVisibleCues[i].data()->setIsActive(false);
+    for (size_t i = 0; i < previouslyActiveCues.size(); ++i) {
+        if (!m_currentlyActiveCues.contains(previouslyActiveCues[i])) {
+            previouslyActiveCues[i].data()->setIsActive(false);
+            activeSetChanged = true;
+        }
     }
-    for (size_t i = 0; i < m_currentlyVisibleCues.size(); ++i) {
-        if (!previouslyVisibleCues.contains(m_currentlyVisibleCues[i]))
-            m_currentlyVisibleCues[i].data()->setIsActive(true);
+    for (size_t i = 0; i < m_currentlyActiveCues.size(); ++i) {
+        if (!previouslyActiveCues.contains(m_currentlyActiveCues[i])) {
+            m_currentlyActiveCues[i].data()->setIsActive(true);
+            activeSetChanged = true;
+        }
     }
     
     // FIXME(72173): Pause the media element for cues going past their endTime
     // during a monotonic time increase.
+
+    if (activeSetChanged && hasMediaControls())
+        mediaControls()->updateTextTrackDisplay();
 }
 
 bool HTMLMediaElement::textTracksAreReady() const
@@ -982,29 +991,29 @@ void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
 
 void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
 {
-    // 4.8.10.12.3 Sourcing out-of-band text tracks
-    // ... when a text track corresponding to a track element is created with text track
-    // mode set to disabled and subsequently changes its text track mode to hidden, showing,
-    // or showing by default for the first time, the user agent must immediately and synchronously
-    // run the following algorithm ...
-    
-    if (track->trackType() != TextTrack::TrackElement)
-        return;
-    
-    HTMLTrackElement* trackElement;
-    for (Node* node = firstChild(); node; node = node->nextSibling()) {
-        if (!node->hasTagName(trackTag))
-            continue;
-        trackElement = static_cast<HTMLTrackElement*>(node);
-        if (trackElement->track() != track)
-            continue;
-
-        // Mark this track as "configured" so configureTextTrack won't change the mode again.
-        trackElement->setHasBeenConfigured(true);
-        if (track->mode() != TextTrack::DISABLED && trackElement->readyState() == HTMLTrackElement::NONE)
-            trackElement->scheduleLoad();
-        break;
+    if (track->trackType() == TextTrack::TrackElement) {
+        // 4.8.10.12.3 Sourcing out-of-band text tracks
+        // ... when a text track corresponding to a track element is created with text track
+        // mode set to disabled and subsequently changes its text track mode to hidden, showing,
+        // or showing by default for the first time, the user agent must immediately and synchronously
+        // run the following algorithm ...
+
+        for (Node* node = firstChild(); node; node = node->nextSibling()) {
+            if (!node->hasTagName(trackTag))
+                continue;
+            HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
+            if (trackElement->track() != track)
+                continue;
+            
+            // Mark this track as "configured" so configureTextTrack won't change the mode again.
+            trackElement->setHasBeenConfigured(true);
+            if (track->mode() != TextTrack::DISABLED && trackElement->readyState() == HTMLTrackElement::NONE)
+                trackElement->scheduleLoad();
+            break;
+        }
     }
+
+    configureTextTrackDisplay();
 }
 
 void HTMLMediaElement::textTrackKindChanged(TextTrack*)
@@ -2289,10 +2298,10 @@ bool HTMLMediaElement::userIsInterestedInThisTrack(HTMLTrackElement* trackElemen
 
     // If ... the user has indicated an interest in having a track with this text track kind, text track language, ... 
     Settings* settings = document()->settings();
+    if (!settings)
+        return false;
 
     if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) {
-        if (!settings)
-            return false;
         if (kind == TextTrack::subtitlesKeyword() && !settings->shouldDisplaySubtitles())
             return false;
         if (kind == TextTrack::captionsKeyword() && !settings->shouldDisplayCaptions())
@@ -2301,7 +2310,7 @@ bool HTMLMediaElement::userIsInterestedInThisTrack(HTMLTrackElement* trackElemen
     }
 
     if (kind == TextTrack::descriptionsKeyword()) {
-        if (!settings || !settings->shouldDisplayTextDescriptions())
+        if (!settings->shouldDisplayTextDescriptions())
             return false;
         return userIsInterestedInThisLanguage(trackElement->srclang());
     }
@@ -3452,6 +3461,7 @@ bool HTMLMediaElement::createMediaControls()
         return false;
 
     controls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
+    controls->reset();
 
     ensureShadowRoot()->appendChild(controls, ec);
     return true;
@@ -3466,11 +3476,9 @@ void HTMLMediaElement::configureMediaControls()
         return;
     }
 
-    if (!hasMediaControls()) {
-        if (!createMediaControls())
-            return;
-        mediaControls()->reset();
-    }
+    if (!hasMediaControls() && !createMediaControls())
+        return;
+
     mediaControls()->show();
 #else
     if (m_player)
@@ -3478,6 +3486,35 @@ void HTMLMediaElement::configureMediaControls()
 #endif
 }
 
+#if ENABLE(VIDEO_TRACK)
+void HTMLMediaElement::configureTextTrackDisplay()
+{
+    ASSERT(m_textTracks);
+
+    bool haveVisibleTextTrack = false;
+    for (unsigned i = 0; i < m_textTracks->length(); ++i) {
+        if (m_textTracks->item(i)->mode() == TextTrack::SHOWING) {
+            haveVisibleTextTrack = true;
+            break;
+        }
+    }
+
+    if (m_haveVisibleTextTrack == haveVisibleTextTrack)
+        return;
+    m_haveVisibleTextTrack = haveVisibleTextTrack;
+
+    if (!m_haveVisibleTextTrack) {
+        if (hasMediaControls())
+            mediaControls()->hideTextTrackDisplay();
+        return;
+    }
+
+    if (!hasMediaControls() && !createMediaControls())
+        return;
+    mediaControls()->showTextTrackDisplay();
+}
+#endif
+
 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
 {
     if (event && event->type() == eventNames().webkitfullscreenchangeEvent)
index 7d3fabd..e1f3514 100644 (file)
@@ -66,6 +66,11 @@ class Widget;
 class DisplaySleepDisabler;
 #endif
 
+#if ENABLE(VIDEO_TRACK)
+typedef PODIntervalTree<double, TextTrackCue*> CueIntervalTree;
+typedef Vector<CueIntervalTree::IntervalType> CueList;
+#endif
+
 // FIXME: The inheritance from MediaPlayerClient here should be private inheritance.
 // But it can't be until the Chromium WebMediaPlayerClientImpl class is fixed so it
 // no longer depends on typecasting a MediaPlayerClient to an HTMLMediaElement.
@@ -196,6 +201,7 @@ public:
     PassRefPtr<TextTrack> addTrack(const String& kind, ExceptionCode& ec) { return addTrack(kind, emptyString(), emptyString(), ec); }
 
     TextTrackList* textTracks();
+    CueList currentlyActiveCues() const { return m_currentlyActiveCues; }
 
     virtual void trackWasAdded(HTMLTrackElement*);
     virtual void trackWillBeRemoved(HTMLTrackElement*);
@@ -203,6 +209,7 @@ public:
     void configureTextTrack(HTMLTrackElement*);
     void configureTextTracks();
     bool textTracksAreReady() const;
+    void configureTextTrackDisplay();
 
     // TextTrackClient
     virtual void textTrackReadyStateChanged(TextTrack*);
@@ -549,12 +556,12 @@ private:
 
 #if ENABLE(VIDEO_TRACK)
     bool m_tracksAreReady : 1;
+    bool m_haveVisibleTextTrack : 1;
+
     RefPtr<TextTrackList> m_textTracks;
     Vector<RefPtr<TextTrack> > m_textTracksWhenResourceSelectionBegan;
-    
-    typedef PODIntervalTree <double, TextTrackCue*> CueIntervalTree;
     CueIntervalTree m_cueTree;
-    Vector<CueIntervalTree::IntervalType> m_currentlyVisibleCues;
+    CueList m_currentlyActiveCues;
 #endif
 
 #if ENABLE(WEB_AUDIO)
index 6565151..a1db7aa 100644 (file)
@@ -47,6 +47,7 @@
 #include "RenderMedia.h"
 #include "RenderSlider.h"
 #include "RenderTheme.h"
+#include "RenderVideo.h"
 #include "RenderView.h"
 #include "ScriptController.h"
 #include "Settings.h"
@@ -1115,6 +1116,112 @@ const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() cons
     return id;
 }
 
+// ----------------------------
+
+#if ENABLE(VIDEO_TRACK)
+
+class RenderTextTrackContainerElement : public RenderBlock {
+public:
+    RenderTextTrackContainerElement(Node*);
+    
+private:
+    virtual void layout();
+};
+
+RenderTextTrackContainerElement::RenderTextTrackContainerElement(Node* node)
+    : RenderBlock(node)
+{
+}
+
+void RenderTextTrackContainerElement::layout()
+{
+    RenderBlock::layout();
+    if (style()->display() == NONE)
+        return;
+
+    ASSERT(mediaControlElementType(node()) == MediaTextTrackDisplayContainer);
+
+    LayoutStateDisabler layoutStateDisabler(view());
+    static_cast<MediaControlTextTrackContainerElement*>(node())->updateSizes();
+}
+
+inline MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document)
+    : MediaControlElement(document)
+    , m_fontSize(0)
+    , m_bottom(0)
+{
+}
+
+PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document)
+{
+    RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
+    element->hide();
+    return element.release();
+}
+
+RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+    return new (arena) RenderTextTrackContainerElement(this);
+}
+
+const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
+{
+    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container"));
+    return id;
+}
+
+static const float mimimumFontSize = 16;
+static const float videoHeightFontSizeDivisor = 25;
+static const float trackBottomMultiplier = 0.9;
+    
+void MediaControlTextTrackContainerElement::updateSizes()
+{
+    HTMLMediaElement* mediaElement = toParentMediaElement(this);
+    if (!mediaElement || !mediaElement->renderer() || !mediaElement->renderer()->isVideo())
+        return;
+    
+    IntRect videoBox = toRenderVideo(mediaElement->renderer())->videoBox();
+    if (m_videoDisplaySize == videoBox)
+        return;
+    m_videoDisplaySize = videoBox;
+
+    float fontSize = m_videoDisplaySize.size().height() / videoHeightFontSizeDivisor;
+    if (fontSize < mimimumFontSize)
+        fontSize = mimimumFontSize;
+    if (fontSize != m_fontSize) {
+        m_fontSize = fontSize;
+        ensureInlineStyleDecl()->setProperty(CSSPropertyFontSize, String::number(fontSize) + "px");
+    }
+
+    LayoutUnit bottom = static_cast<LayoutUnit>(m_videoDisplaySize.y() + m_videoDisplaySize.height() - (m_videoDisplaySize.height() * trackBottomMultiplier));
+    if (bottom != m_bottom) {
+        m_bottom = bottom;
+        ensureInlineStyleDecl()->setProperty(CSSPropertyBottom, String::number(bottom) + "px");
+    }
+}
+
+// ----------------------------
+
+MediaControlTextTrackDisplayElement::MediaControlTextTrackDisplayElement(Document* document)
+    : MediaControlElement(document)
+{
+}
+
+PassRefPtr<MediaControlTextTrackDisplayElement> MediaControlTextTrackDisplayElement::create(Document* document)
+{
+    return adoptRef(new MediaControlTextTrackDisplayElement(document));
+}
+
+const AtomicString& MediaControlTextTrackDisplayElement::shadowPseudoId() const
+{
+    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-display"));
+    return id;
+}
+
+#endif
+
+// ----------------------------
+
 } // namespace WebCore
 
 #endif // ENABLE(VIDEO)
index c4d52bf..8e67d00 100644 (file)
@@ -69,6 +69,8 @@ enum MediaControlElementType {
     MediaVolumeSlider,
     MediaVolumeSliderThumb,
     MediaVolumeSliderMuteButton,
+    MediaTextTrackDisplayContainer,
+    MediaTextTrackDisplay,
 };
 
 HTMLMediaElement* toParentMediaElement(Node*);
@@ -484,6 +486,42 @@ private:
  
 // ----------------------------
 
+#if ENABLE(VIDEO_TRACK)
+class MediaControlTextTrackContainerElement : public MediaControlElement {
+public:
+    
+    static PassRefPtr<MediaControlTextTrackContainerElement> create(Document*);
+    
+    void updateSizes();
+
+private:
+    MediaControlTextTrackContainerElement(Document*);
+
+    virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+    virtual MediaControlElementType displayType() const { return MediaTextTrackDisplayContainer; }
+    virtual const AtomicString& shadowPseudoId() const;
+
+    IntRect m_videoDisplaySize;
+    float m_fontSize;
+    LayoutUnit m_bottom;
+};
+
+// ----------------------------
+
+class MediaControlTextTrackDisplayElement : public MediaControlElement {
+public:
+    static PassRefPtr<MediaControlTextTrackDisplayElement> create(Document*);
+
+private:
+    MediaControlTextTrackDisplayElement(Document*);
+
+    virtual MediaControlElementType displayType() const { return MediaTextTrackDisplay; }
+    virtual const AtomicString& shadowPseudoId() const;
+};
+#endif
+
+// ----------------------------
+
 } // namespace WebCore
 
 #endif // ENABLE(VIDEO)
index 075b438..0d356ce 100644 (file)
 
 #include "Chrome.h"
 #include "HTMLMediaElement.h"
+#include "HTMLNames.h"
 #include "MediaControlElements.h"
 #include "MouseEvent.h"
 #include "Page.h"
 #include "RenderTheme.h"
+#include "Text.h"
+
+#if ENABLE(VIDEO_TRACK)
+#include "TextTrackCue.h"
+#endif
 
 using namespace std;
 
@@ -65,8 +71,12 @@ MediaControlRootElement::MediaControlRootElement(Document* document)
     , m_fullScreenVolumeSlider(0)
     , m_fullScreenMaxVolumeButton(0)
     , m_panel(0)
-    , m_isMouseOverControls(false)
+#if ENABLE(VIDEO_TRACK)
+    , m_textDisplayContainer(0)
+    , m_textTrackDisplay(0)
+#endif
     , m_hideFullscreenControlsTimer(this, &MediaControlRootElement::hideFullscreenControlsTimerFired)
+    , m_isMouseOverControls(false)
 {
 }
 
@@ -264,6 +274,12 @@ void MediaControlRootElement::setMediaController(MediaControllerInterface* contr
         m_fullScreenMaxVolumeButton->setMediaController(controller);
     if (m_panel)
         m_panel->setMediaController(controller);
+#if ENABLE(VIDEO_TRACK)
+    if (m_textDisplayContainer)
+        m_textDisplayContainer->setMediaController(controller);
+    if (m_textTrackDisplay)
+        m_textTrackDisplay->setMediaController(controller);
+#endif
     reset();
 }
 
@@ -576,6 +592,75 @@ void MediaControlRootElement::stopHideFullscreenControlsTimer()
     m_hideFullscreenControlsTimer.stop();
 }
 
+#if ENABLE(VIDEO_TRACK)
+void MediaControlRootElement::createTextTrackDisplay()
+{
+    if (m_textDisplayContainer)
+        return;
+
+    RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
+    m_textDisplayContainer = textDisplayContainer.get();
+
+    RefPtr<MediaControlTextTrackDisplayElement> textDisplay = MediaControlTextTrackDisplayElement::create(document());
+    m_textDisplayContainer->hide();
+    m_textTrackDisplay = textDisplay.get();
+
+    ExceptionCode ec;
+    textDisplayContainer->appendChild(textDisplay.release(), ec, true);
+    if (ec)
+        return;
+
+    // Insert it before the first controller element so it always displays behind the controls.
+    insertBefore(textDisplayContainer.release(), m_panel, ec, true);
+}
+
+void MediaControlRootElement::showTextTrackDisplay()
+{
+    if (!m_textDisplayContainer)
+        createTextTrackDisplay();
+    m_textDisplayContainer->show();
+}
+
+void MediaControlRootElement::hideTextTrackDisplay()
+{
+    if (!m_textDisplayContainer)
+        createTextTrackDisplay();
+    m_textDisplayContainer->hide();
+}
+
+void MediaControlRootElement::updateTextTrackDisplay()
+{
+    if (!m_textDisplayContainer)
+        createTextTrackDisplay();
+
+    CueList activeCues = toParentMediaElement(m_textDisplayContainer)->currentlyActiveCues();
+    if (activeCues.isEmpty())
+        return;
+
+    m_textTrackDisplay->removeChildren();
+    bool nothingToDisplay = true;
+    for (size_t i = 0; i < activeCues.size(); ++i) {
+        TextTrackCue* cue = activeCues[i].data();
+        ASSERT(cue->isActive());
+        if (!cue->track() || cue->track()->mode() != TextTrack::SHOWING)
+            continue;
+
+        String cueText = cue->getCueAsSource();
+        if (!cueText.isEmpty()) {
+            if (!nothingToDisplay)
+                m_textTrackDisplay->appendChild(document()->createElement(HTMLNames::brTag, false), ASSERT_NO_EXCEPTION);
+            m_textTrackDisplay->appendChild(document()->createTextNode(cueText), ASSERT_NO_EXCEPTION);
+            nothingToDisplay = false;
+        }
+    }
+
+    if (!nothingToDisplay)
+        m_textDisplayContainer->show();
+    else
+        m_textDisplayContainer->hide();
+}
+#endif
+
 const AtomicString& MediaControlRootElement::shadowPseudoId() const
 {
     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
index ed90c58..7010d02 100644 (file)
@@ -66,6 +66,11 @@ class MediaPlayer;
 class RenderBox;
 class RenderMedia;
 
+#if ENABLE(VIDEO_TRACK)
+class MediaControlTextTrackContainerElement;
+class MediaControlTextTrackDisplayElement;
+#endif
+
 class MediaControlRootElement : public MediaControls {
 public:
     static PassRefPtr<MediaControlRootElement> create(Document*);
@@ -98,6 +103,13 @@ public:
     void updateTimeDisplay();
     void updateStatusDisplay();
 
+#if ENABLE(VIDEO_TRACK)
+    void createTextTrackDisplay();
+    void showTextTrackDisplay();
+    void hideTextTrackDisplay();
+    void updateTextTrackDisplay();
+#endif
+
     virtual bool shouldHideControls();
 
 private:
@@ -134,8 +146,12 @@ private:
     MediaControlFullscreenVolumeSliderElement* m_fullScreenVolumeSlider;
     MediaControlFullscreenVolumeMaxButtonElement* m_fullScreenMaxVolumeButton;
     MediaControlPanelElement* m_panel;
-    bool m_isMouseOverControls;
+#if ENABLE(VIDEO_TRACK)
+    MediaControlTextTrackContainerElement* m_textDisplayContainer;
+    MediaControlTextTrackDisplayElement* m_textTrackDisplay;
+#endif
     Timer<MediaControlRootElement> m_hideFullscreenControlsTimer;
+    bool m_isMouseOverControls;
 };
 
 }
index b923bb0..a1026cc 100644 (file)
 #if ENABLE(VIDEO)
 #include "MediaControlRootElementChromium.h"
 
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
 #include "MediaControlElements.h"
 #include "MouseEvent.h"
 #include "Page.h"
 #include "RenderTheme.h"
+#include "Text.h"
+
+#if ENABLE(VIDEO_TRACK)
+#include "TextTrackCue.h"
+#endif
 
 using namespace std;
 
@@ -49,6 +56,10 @@ MediaControlRootElementChromium::MediaControlRootElementChromium(Document* docum
     , m_volumeSlider(0)
     , m_volumeSliderContainer(0)
     , m_panel(0)
+#if ENABLE(VIDEO_TRACK)
+    , m_textDisplayContainer(0)
+    , m_textTrackDisplay(0)
+#endif
     , m_opaque(true)
     , m_isMouseOverControls(false)
 {
@@ -144,6 +155,12 @@ void MediaControlRootElementChromium::setMediaController(MediaControllerInterfac
         m_volumeSliderContainer->setMediaController(controller);
     if (m_panel)
         m_panel->setMediaController(controller);
+#if ENABLE(VIDEO_TRACK)
+    if (m_textDisplayContainer)
+        m_textDisplayContainer->setMediaController(controller);
+    if (m_textTrackDisplay)
+        m_textTrackDisplay->setMediaController(controller);
+#endif
     reset();
 }
 
@@ -309,6 +326,75 @@ void MediaControlRootElementChromium::showVolumeSlider()
     m_volumeSliderContainer->show();
 }
 
+#if ENABLE(VIDEO_TRACK)
+void MediaControlRootElementChromium::createTextTrackDisplay()
+{
+    if (m_textDisplayContainer)
+        return;
+
+    RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
+    m_textDisplayContainer = textDisplayContainer.get();
+
+    RefPtr<MediaControlTextTrackDisplayElement> textDisplay = MediaControlTextTrackDisplayElement::create(document());
+    m_textDisplayContainer->hide();
+    m_textTrackDisplay = textDisplay.get();
+
+    ExceptionCode ec;
+    textDisplayContainer->appendChild(textDisplay.release(), ec, true);
+    if (ec)
+        return;
+
+    // Insert it before the first controller element so it always displays behind the controls.
+    insertBefore(textDisplayContainer.release(), m_panel, ec, true);
+}
+
+void MediaControlRootElementChromium::showTextTrackDisplay()
+{
+    if (!m_textDisplayContainer)
+        createTextTrackDisplay();
+    m_textDisplayContainer->show();
+}
+
+void MediaControlRootElementChromium::hideTextTrackDisplay()
+{
+    if (!m_textDisplayContainer)
+        createTextTrackDisplay();
+    m_textDisplayContainer->hide();
+}
+
+void MediaControlRootElementChromium::updateTextTrackDisplay()
+{
+    if (!m_textDisplayContainer)
+        createTextTrackDisplay();
+
+    CueList activeCues = toParentMediaElement(m_textDisplayContainer)->currentlyActiveCues();
+    if (activeCues.isEmpty())
+        return;
+
+    m_textTrackDisplay->removeChildren();
+    bool nothingToDisplay = true;
+    for (size_t i = 0; i < activeCues.size(); ++i) {
+        TextTrackCue* cue = activeCues[i].data();
+        ASSERT(cue->isActive());
+        if (!cue->track() || cue->track()->mode() != TextTrack::SHOWING)
+            continue;
+
+        String cueText = cue->getCueAsSource();
+        if (!cueText.isEmpty()) {
+            if (!nothingToDisplay)
+                m_textTrackDisplay->appendChild(document()->createElement(HTMLNames::brTag, false), ASSERT_NO_EXCEPTION);
+            m_textTrackDisplay->appendChild(document()->createTextNode(cueText), ASSERT_NO_EXCEPTION);
+            nothingToDisplay = false;
+        }
+    }
+
+    if (!nothingToDisplay)
+        m_textDisplayContainer->show();
+    else
+        m_textDisplayContainer->hide();
+}
+#endif
+    
 const AtomicString& MediaControlRootElementChromium::shadowPseudoId() const
 {
     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
index 5082720..d895c3b 100644 (file)
@@ -55,6 +55,11 @@ class MediaPlayer;
 class RenderBox;
 class RenderMedia;
 
+#if ENABLE(VIDEO_TRACK)
+class MediaControlTextTrackContainerElement;
+class MediaControlTextTrackDisplayElement;
+#endif
+
 class MediaControlRootElementChromium : public MediaControls {
 public:
     static PassRefPtr<MediaControlRootElementChromium> create(Document*);
@@ -87,6 +92,13 @@ public:
     void updateTimeDisplay();
     void updateStatusDisplay();
 
+#if ENABLE(VIDEO_TRACK)
+    void createTextTrackDisplay();
+    void showTextTrackDisplay();
+    void hideTextTrackDisplay();
+    void updateTextTrackDisplay();
+#endif
+
     virtual bool shouldHideControls();
 
 private:
@@ -108,6 +120,10 @@ private:
     MediaControlVolumeSliderElement* m_volumeSlider;
     MediaControlVolumeSliderContainerElement* m_volumeSliderContainer;
     MediaControlPanelElement* m_panel;
+#if ENABLE(VIDEO_TRACK)
+    MediaControlTextTrackContainerElement* m_textDisplayContainer;
+    MediaControlTextTrackDisplayElement* m_textTrackDisplay;
+#endif
 
     bool m_opaque;
     bool m_isMouseOverControls;
index 4f601fd..a61e34e 100644 (file)
@@ -72,6 +72,12 @@ class MediaControls : public HTMLDivElement {
 
     virtual bool shouldHideControls() = 0;
 
+#if ENABLE(VIDEO_TRACK)
+    virtual void showTextTrackDisplay() = 0;
+    virtual void hideTextTrackDisplay() = 0;
+    virtual void updateTextTrackDisplay() = 0;
+#endif
+
 protected:
     MediaControls(Document*);
 
index 92aa2ab..97d2774 100644 (file)
@@ -174,6 +174,9 @@ bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, R
             break;
         case MediaControlsPanel:
             ASSERT_NOT_REACHED();
+        case MediaTextTrackDisplayContainer:
+        case MediaTextTrackDisplay:
+            ASSERT_NOT_REACHED();
             break;
     }
 
index e804771..8a25805 100644 (file)
@@ -270,6 +270,8 @@ bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType
     case MediaStatusDisplay:
     case MediaShowClosedCaptionsButton:
     case MediaHideClosedCaptionsButton:
+    case MediaTextTrackDisplayContainer:
+    case MediaTextTrackDisplay:
         ASSERT_NOT_REACHED();
         break;
     }