Source/WebCore/ChangeLog

 12012-06-03 Victor Carbune <vcarbune@adobe.com>
 2
 3 Need a short description and bug URL (OOPS!)
 4
 5 Reviewed by NOBODY (OOPS!).
 6
 7 Test: media/track/track-cue-rendering-inner-timestamps.html
 8
 9 * css/mediaControls.css:
 10 (video::-webkit-media-text-track-past-nodes):
 11 (video::-webkit-media-text-track-future-nodes):
 12 * html/HTMLMediaElement.cpp:
 13 (WebCore::HTMLMediaElement::updateActiveTextTrackCues):
 14 * html/track/TextTrackCue.cpp:
 15 (WebCore::TextTrackCue::TextTrackCue):
 16 (WebCore::TextTrackCue::getCueAsHTML):
 17 (WebCore::TextTrackCue::updateDisplayTree):
 18 (WebCore):
 19 (WebCore::TextTrackCue::getDisplayTree):
 20 * html/track/TextTrackCue.h:
 21 (TextTrackCue):
 22 * html/track/WebVTTParser.cpp:
 23 (WebCore::WebVTTParser::constructTreeFromToken):
 24 * html/track/WebVTTParser.h:
 25 (WebVTTParser):
 26
 272012-06-03 Victor Carbune <victor@rosedu.org>
 28
 29 Basic support for timestamps within a TextTrackCue
 30 https://bugs.webkit.org/show_bug.cgi?id=88187
 31
 32 Implemented support for timestamps within a TextTrackCue.
 33 This enables rendering functionality for Karaoke and Paint-on captions.
 34
 35 Reviewed by NOBODY (OOPS!).
 36
 37 Test: media/track/track-cue-rendering-inner-timestamps.html
 38
 39 * css/mediaControls.css: Remove the background shadow pseudo-id and
 40 added two others, which can be further used for styling past / future
 41 contents of the cue.
 42 (video::-webkit-media-text-track-past-nodes): Used to style elements
 43 within the cue that are before the current movie time.
 44 (video::-webkit-media-text-track-future-nodes): Used to style elements
 45 within the cue that are after the current movie time.
 46 * html/HTMLMediaElement.cpp:
 47 (WebCore::HTMLMediaElement::updateActiveTextTrackCues): Regardless of whether
 48 the active set changed or not, the current cues need to be informed of the current
 49 movie time.
 50 * html/track/TextTrackCue.cpp:
 51 (WebCore::TextTrackCue::TextTrackCue): Initiliazed past and future containers.
 52 (WebCore::TextTrackCue::getCueAsHTML): Checked whether the current cue text has
 53 inner timestamps or not, in order to render faster if no timestamps are there.
 54 (WebCore::TextTrackCue::updateDisplayTree): Added method that keeps the past and future
 55 containers up to date, given a current movie time.
 56 (WebCore):
 57 (WebCore::TextTrackCue::getDisplayTree): Replaced the cue background container with the
 58 past cues container. The *internal* display tree of the cue now has two containers,
 59 representing past and future elements.
 60 * html/track/TextTrackCue.h: Added several variables to support the new functionality.
 61 (TextTrackCue):
 62 * html/track/WebVTTParser.cpp:
 63 (WebCore::WebVTTParser::constructTreeFromToken): Updated representation for this patch.
 64 * html/track/WebVTTParser.h:
 65 (WebVTTParser): Made public the collectTimeStamp method, as this is required for
 66 parsing timestamps within a cue as well.
 67
1682012-06-03 Sheriff Bot <webkit.review.bot@gmail.com>
269
370 Unreviewed, rolling out r119359.

Source/WebCore/css/mediaControls.css

@@video::-webkit-media-text-track-container {
225225 -webkit-box-flex: 1;
226226}
227227
228 video::-webkit-media-text-track-background {
 228video::-webkit-media-text-track-past-nodes {
229229 display: inline;
 230
230231 background-color: rgba(0, 0, 0, 0.8);
231232 padding: 2px 2px;
 233
 234 white-space: pre-wrap;
 235}
 236
 237video::-webkit-media-text-track-future-nodes {
 238 visibility: hidden;
 239 display: inline;
 240
 241 color: green;
 242 background-color: rgba(0, 0, 0, 0.8);
 243
 244 margin-left: -2px;
 245 padding: 2px 2px 2px 0px;
 246
232247 white-space: pre-wrap;
233248}
234249

Source/WebCore/html/HTMLMediaElement.cpp

@@void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
11011101 if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
11021102 activeSetChanged = true;
11031103
1104  for (size_t i = 0; !activeSetChanged && i < currentCuesSize; ++i) {
 1104 for (size_t i = 0; i < currentCuesSize; ++i) {
 1105 currentCues[i].data()->updateDisplayTree(movieTime);
 1106
11051107 if (!currentCues[i].data()->isActive())
11061108 activeSetChanged = true;
11071109 }

Source/WebCore/html/track/TextTrackCue.cpp

4040#include "DocumentFragment.h"
4141#include "Event.h"
4242#include "HTMLDivElement.h"
 43#include "HTMLMediaElement.h"
4344#include "Text.h"
4445#include "TextTrack.h"
4546#include "TextTrackCueList.h"

@@TextTrackCue::TextTrackCue(ScriptExecutionContext* context, const String& id, do
99100 , m_cueIndex(invalidCueIndex)
100101 , m_writingDirection(Horizontal)
101102 , m_cueAlignment(Middle)
 103 , m_documentFragment(0)
102104 , m_scriptExecutionContext(context)
103105 , m_isActive(false)
104106 , m_pauseOnExit(pauseOnExit)
105107 , m_snapToLines(true)
 108 , m_hasInnerTimestamps(false)
 109 , m_pastDocumentNodes(HTMLDivElement::create(static_cast<Document*>(context)))
 110 , m_futureDocumentNodes(HTMLDivElement::create(static_cast<Document*>(context)))
106111 , m_displayTreeShouldChange(true)
107112 , m_displayTree(HTMLDivElement::create(static_cast<Document*>(context)))
108113 , m_displayXPosition(undefinedPosition)

@@PassRefPtr<DocumentFragment> TextTrackCue::getCueAsHTML()
372377 RefPtr<DocumentFragment> clonedFragment;
373378 Document* document;
374379
375  if (!m_documentFragment)
 380 if (!m_documentFragment) {
 381 m_hasInnerTimestamps = false;
376382 m_documentFragment = WebVTTParser::create(0, m_scriptExecutionContext)->createDocumentFragmentFromCueText(m_content);
377383
 384 for (Node *child = m_documentFragment->firstChild(); !m_hasInnerTimestamps && child; child = child->nextSibling()) {
 385 if (child->nodeName() == "timestamp")
 386 m_hasInnerTimestamps = true;
 387 }
 388 }
 389
378390 document = static_cast<Document*>(m_scriptExecutionContext);
379391
380392 clonedFragment = DocumentFragment::create(document);

@@void TextTrackCue::calculateDisplayParameters()
528540 // FIXME(Bug 79916): CSS top and left properties need to be applied.
529541}
530542
 543void TextTrackCue::updateDisplayTree(float movieTime)
 544{
 545 // The display tree may contain WebVTT timestamp objects representing
 546 // timestamps (processing instructions), along with displayable nodes.
 547 DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp"));
 548
 549 DEFINE_STATIC_LOCAL(const AtomicString, trackPastNodesShadowPseudoId, ("-webkit-media-text-track-past-nodes"));
 550 DEFINE_STATIC_LOCAL(const AtomicString, trackFutureNodesShadowPseudoId, ("-webkit-media-text-track-future-nodes"));
 551
 552 bool isPastNode = true;
 553
 554 // Clear the contents of the two sets.
 555 m_futureDocumentNodes->removeChildren();
 556 m_futureDocumentNodes->setShadowPseudoId(trackFutureNodesShadowPseudoId);
 557
 558 m_pastDocumentNodes->removeChildren();
 559 m_pastDocumentNodes->setShadowPseudoId(trackPastNodesShadowPseudoId);
 560
 561 // Update the two sets containing past and future WebVTT objects.
 562 RefPtr<DocumentFragment> referenceTree = getCueAsHTML();
 563
 564 if (!m_hasInnerTimestamps) {
 565 m_pastDocumentNodes->appendChild(referenceTree);
 566 return;
 567 }
 568
 569 for (Node *child = referenceTree->firstChild(); child; child = child->nextSibling()) {
 570 if (child->nodeName() == timestampTag) {
 571 unsigned int position = 0;
 572 String timestamp = child->nodeValue();
 573
 574 double timestampTime = WebVTTParser::create(0, m_scriptExecutionContext)->collectTimeStamp(timestamp, &position);
 575 ASSERT(timestampTime != -1);
 576
 577 if (timestampTime > movieTime)
 578 isPastNode = false;
 579 }
 580
 581 if (isPastNode)
 582 m_pastDocumentNodes->appendChild(child->cloneNode(true), ASSERT_NO_EXCEPTION, false);
 583 else
 584 m_futureDocumentNodes->appendChild(child->cloneNode(true), ASSERT_NO_EXCEPTION, false);
 585 }
 586}
 587
531588PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree()
532589{
533  DEFINE_STATIC_LOCAL(const AtomicString, trackBackgroundShadowPseudoId, ("-webkit-media-text-track-background"));
534590 DEFINE_STATIC_LOCAL(const AtomicString, trackDisplayBoxShadowPseudoId, ("-webkit-media-text-track-display"));
535591
536592 if (!m_displayTreeShouldChange)

@@PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree()
542598 // 10.11. Apply the terms of the CSS specifications to nodes within the
543599 // following constraints, thus obtaining a set of CSS boxes positioned
544600 // relative to an initial containing block:
 601 m_displayTree->setShadowPseudoId(trackDisplayBoxShadowPseudoId, ASSERT_NO_EXCEPTION);
545602 m_displayTree->removeChildren();
546603
547604 // The document tree is the tree of WebVTT Node Objects rooted at nodes.

@@PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree()
549606 // The children of the nodes must be wrapped in an anonymous box whose
550607 // 'display' property has the value 'inline'. This is the WebVTT cue
551608 // background box.
552  RefPtr<HTMLDivElement> cueBackgroundBox = HTMLDivElement::create(static_cast<Document*>(m_scriptExecutionContext));
553609
554  cueBackgroundBox->setShadowPseudoId(trackBackgroundShadowPseudoId);
555  cueBackgroundBox->appendChild(getCueAsHTML(), ASSERT_NO_EXCEPTION, true);
556 
557  m_displayTree->setShadowPseudoId(trackDisplayBoxShadowPseudoId, ASSERT_NO_EXCEPTION);
558  m_displayTree->appendChild(cueBackgroundBox, ASSERT_NO_EXCEPTION, true);
 610 // Note: This is contained by default in m_pastDocumentNodes.
 611 m_displayTree->appendChild(m_pastDocumentNodes, ASSERT_NO_EXCEPTION, true);
 612 m_displayTree->appendChild(m_futureDocumentNodes, ASSERT_NO_EXCEPTION, true);
559613
560614 // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not
561615 // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose

@@PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree()
601655 CSSValuePre);
602656 }
603657
 658 if (m_hasInnerTimestamps)
 659 updateDisplayTree(track()->mediaElement()->currentTime());
 660
604661 m_displayTreeShouldChange = false;
605662
606663 // 10.15. Let cue's text track cue display state have the CSS boxes in

Source/WebCore/html/track/TextTrackCue.h

@@public:
102102 void setIsActive(bool);
103103
104104 PassRefPtr<HTMLDivElement> getDisplayTree();
 105 void updateDisplayTree(float);
105106 void removeDisplayTree();
106107
107108 virtual const AtomicString& interfaceName() const;

@@private:
167168 bool m_pauseOnExit;
168169 bool m_snapToLines;
169170
 171 bool m_hasInnerTimestamps;
 172 RefPtr<HTMLDivElement> m_pastDocumentNodes;
 173 RefPtr<HTMLDivElement> m_futureDocumentNodes;
 174
170175 bool m_displayTreeShouldChange;
171176 RefPtr<HTMLDivElement> m_displayTree;
172177

Source/WebCore/html/track/WebVTTParser.cpp

@@void WebVTTParser::constructTreeFromToken(Document* document)
383383 unsigned position = 0;
384384 double time = collectTimeStamp(m_token.characters().data(), &position);
385385 if (time != malformedTime)
386  m_currentNode->parserAddChild(ProcessingInstruction::create(document, "timestamp", String(m_token.characters().data(), m_token.characters().size())));
 386 m_currentNode->parserAddChild(ProcessingInstruction::create(document, "timestamp", String(m_token.characters().data(), m_token.characters().size())));
387387 break;
388388 }
389389 default:

Source/WebCore/html/track/WebVTTParser.h

@@public:
9494 void getNewCues(Vector<RefPtr<TextTrackCue> >&);
9595
9696 PassRefPtr<DocumentFragment> createDocumentFragmentFromCueText(const String&);
 97 double collectTimeStamp(const String&, unsigned*);
9798
9899protected:
99100 WebVTTParser(WebVTTParserClient*, ScriptExecutionContext*);

@@private:
110111
111112 void createNewCue();
112113 void resetCueValues();
113  double collectTimeStamp(const String&, unsigned*);
 114
114115 void skipWhiteSpace(const String&, unsigned*);
115116 static void skipLineTerminator(const char* data, unsigned length, unsigned*);
116117 static String collectNextLine(const char* data, unsigned length, unsigned*);

LayoutTests/ChangeLog

 12012-06-03 Victor Carbune <victor@rosedu.org>
 2
 3 Basic support for timestamps within a TextTrackCue
 4 https://bugs.webkit.org/show_bug.cgi?id=88187
 5
 6 Implemented support for timestamps within a TextTrackCue.
 7 This enables rendering functionality for Karaoke and Paint-on captions.
 8
 9 Reviewed by NOBODY (OOPS!).
 10
 11 * media/track/captions-webvtt/captions-inner-timestamps.vtt: Added.
 12 * media/track/track-cue-rendering-inner-timestamps-expected.txt: Added.
 13 * media/track/track-cue-rendering-inner-timestamps.html: Added.
 14
1152012-06-03 Sheriff Bot <webkit.review.bot@gmail.com>
216
317 Unreviewed, rolling out r119359.

LayoutTests/media/track/captions-webvtt/captions-inner-timestamps.vtt

 1WEBVTT
 2
 31
 400:00:00.000 --> 00:00:00.950
 5<00:00:00.200>Chocolate <00:00:00.450>Rain <00:00:00.700>Karaoke
 6
 72
 800:00:01.000 --> 00:00:03.600
 9<00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 10
 113
 1200:00:03.700 --> 00:00:04.250
 13<00:00:03.750>Chocolate <00:00:04.000>Rain

LayoutTests/media/track/track-cue-rendering-inner-timestamps-expected.txt

 1Test that TextTrack's cues are rendered correctly when they contain inner timestamps.
 2EVENT(canplaythrough)
 3
 4** First cue (empty) **
 5EVENT(seeked)
 6
 7* Cue 0 *
 8Current time: 0
 9Past:
 10Future: Chocolate Rain Karaoke
 11Complete: <00:00:00.200>Chocolate <00:00:00.450>Rain <00:00:00.700>Karaoke
 12
 13** First cue (one word) **
 14EVENT(seeked)
 15
 16* Cue 0 *
 17Current time: 0.25
 18Past: Chocolate
 19Future: Rain Karaoke
 20Complete: <00:00:00.200>Chocolate <00:00:00.450>Rain <00:00:00.700>Karaoke
 21
 22** First cue (two words) **
 23EVENT(seeked)
 24
 25* Cue 0 *
 26Current time: 0.5
 27Past: Chocolate Rain
 28Future: Karaoke
 29Complete: <00:00:00.200>Chocolate <00:00:00.450>Rain <00:00:00.700>Karaoke
 30
 31** First cue (three words) **
 32EVENT(seeked)
 33
 34* Cue 0 *
 35Current time: 0.75
 36Past: Chocolate Rain Karaoke
 37Future:
 38Complete: <00:00:00.200>Chocolate <00:00:00.450>Rain <00:00:00.700>Karaoke
 39
 40** Second cue (empty) **
 41EVENT(seeked)
 42
 43* Cue 0 *
 44Current time: 1
 45Past:
 46Future: Some stay dry and others feel the pain
 47Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 48
 49** Second cue (one word) **
 50EVENT(seeked)
 51
 52* Cue 0 *
 53Current time: 1.25
 54Past: Some
 55Future: stay dry and others feel the pain
 56Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 57
 58** Second cue (two words) **
 59EVENT(seeked)
 60
 61* Cue 0 *
 62Current time: 1.5
 63Past: Some stay
 64Future: dry and others feel the pain
 65Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 66
 67** Second cue (three words) **
 68EVENT(seeked)
 69
 70* Cue 0 *
 71Current time: 1.75
 72Past: Some stay dry
 73Future: and others feel the pain
 74Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 75
 76** Second cue (same as before) **
 77EVENT(seeked)
 78
 79* Cue 0 *
 80Current time: 2
 81Past: Some stay dry
 82Future: and others feel the pain
 83Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 84
 85** Second cue (four words) **
 86EVENT(seeked)
 87
 88* Cue 0 *
 89Current time: 2.25
 90Past: Some stay dry and
 91Future: others feel the pain
 92Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 93
 94** Second cue (five words) **
 95EVENT(seeked)
 96
 97* Cue 0 *
 98Current time: 2.5
 99Past: Some stay dry and others
 100Future: feel the pain
 101Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 102
 103** Second cue (same as before) **
 104EVENT(seeked)
 105
 106* Cue 0 *
 107Current time: 2.75
 108Past: Some stay dry and others
 109Future: feel the pain
 110Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 111
 112** Second cue (six words) **
 113EVENT(seeked)
 114
 115* Cue 0 *
 116Current time: 3
 117Past: Some stay dry and others feel
 118Future: the pain
 119Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 120
 121** Second cue (seven words) **
 122EVENT(seeked)
 123
 124* Cue 0 *
 125Current time: 3.25
 126Past: Some stay dry and others feel the
 127Future: pain
 128Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 129
 130** Second cue (eight words) **
 131EVENT(seeked)
 132
 133* Cue 0 *
 134Current time: 3.5
 135Past: Some stay dry and others feel the pain
 136Future:
 137Complete: <00:00:01.200>Some <00:00:01.450>stay <00:00:01.700>dry <00:00:02.200>and <00:00:02.450>others <00:00:02.950>feel <00:00:03.200>the <00:00:03.450>pain
 138
 139** Third cue (one word) **
 140EVENT(seeked)
 141
 142* Cue 0 *
 143Current time: 3.75
 144Past: Chocolate
 145Future: Rain
 146Complete: <00:00:03.750>Chocolate <00:00:04.000>Rain
 147
 148** Third cue (two words) **
 149EVENT(seeked)
 150
 151* Cue 0 *
 152Current time: 4
 153Past: Chocolate Rain
 154Future:
 155Complete: <00:00:03.750>Chocolate <00:00:04.000>Rain
 156END OF TEST
 157

LayoutTests/media/track/track-cue-rendering-inner-timestamps.html

 1<!DOCTYPE html>
 2<html>
 3 <head>
 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 5
 6 <script src=../media-file.js></script>
 7 <script src=../video-test.js></script>
 8 <script src=../media-controls.js></script>
 9
 10 <script>
 11
 12 var testTrack;
 13 var cueDisplayElement;
 14 var cueDisplayContainer;
 15 var seekedCount = 0;
 16
 17 var testCaseDescription = [
 18 "First cue (empty)",
 19 "First cue (one word)",
 20 "First cue (two words)",
 21 "First cue (three words)",
 22 "Second cue (empty)",
 23 "Second cue (one word)",
 24 "Second cue (two words)",
 25 "Second cue (three words)",
 26 "Second cue (same as before)",
 27 "Second cue (four words)",
 28 "Second cue (five words)",
 29 "Second cue (same as before)",
 30 "Second cue (six words)",
 31 "Second cue (seven words)",
 32 "Second cue (eight words)",
 33 "Third cue (one word)",
 34 "Third cue (two words)"
 35 ];
 36
 37 // Each test case is found by incrementing with 0.250 the current time.
 38 var testCaseSeekTime = [
 39 ];
 40
 41 var currentTestCase = 0;
 42
 43 function seeked()
 44 {
 45 for (var i = 0; i < testTrack.track.activeCues.length; ++i) {
 46 cueDisplayElement = textTrackDisplayElement(video, 'display', i);
 47
 48 consoleWrite("");
 49 consoleWrite("* Cue " + i + " *");
 50
 51 consoleWrite("Current time: " + video.currentTime);
 52 consoleWrite("Past: " + cueDisplayElement.childNodes[0].innerHTML);
 53 consoleWrite("Future: " + cueDisplayElement.childNodes[1].innerHTML);
 54
 55 consoleWrite("Complete: " + testTrack.track.activeCues[i].text);
 56 }
 57
 58 runNextTestCase();
 59 };
 60
 61 function runNextTestCase()
 62 {
 63 if (currentTestCase == testCaseDescription.length) {
 64 endTest();
 65 return;
 66 }
 67
 68 consoleWrite("");
 69 consoleWrite("** " + testCaseDescription[currentTestCase] + " **");
 70
 71 video.currentTime = testCaseSeekTime[currentTestCase];
 72 waitForEvent('seeked', seeked, false, true);
 73
 74 currentTestCase++;
 75 }
 76
 77 function loaded()
 78 {
 79 consoleWrite("Test that TextTrack's cues are rendered correctly when they contain inner timestamps.");
 80
 81 findMediaElement();
 82 testTrack = document.querySelector('track');
 83 video.src = findMediaFile('video', '../content/test');
 84
 85 waitForEvent('canplaythrough', function() {
 86 cueDisplayContainer = textTrackDisplayElement(video);
 87
 88 for (i = 0.0; i < 4.500; i += 0.250)
 89 testCaseSeekTime.push(i);
 90
 91 runNextTestCase();
 92 });
 93 }
 94
 95 </script>
 96 </head>
 97 <body onload="loaded()">
 98 <video controls>
 99 <track src="captions-webvtt/captions-inner-timestamps.vtt" kind="captions" default>
 100 </video>
 101 </body>
 102</html>