15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2011 Google Inc.  All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions are
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * met:
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     * Redistributions of source code must retain the above copyright
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer.
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     * Redistributions in binary form must reproduce the above
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * copyright notice, this list of conditions and the following disclaimer
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * in the documentation and/or other materials provided with the
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * distribution.
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     * Neither the name of Google Inc. nor the names of its
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * contributors may be used to endorse or promote products derived from
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * this software without specific prior written permission.
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
3251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "core/html/track/vtt/VTTParser.h"
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
34bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)#include "core/dom/Document.h"
3553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/dom/ProcessingInstruction.h"
3653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/dom/Text.h"
3751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "core/html/track/vtt/VTTElement.h"
3809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)#include "core/html/track/vtt/VTTScanner.h"
395d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)#include "platform/RuntimeEnabledFeatures.h"
401e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "platform/text/SegmentedString.h"
41e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch#include "wtf/text/WTFString.h"
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
43c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink {
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)using namespace HTMLNames;
4609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const double secondsPerHour = 3600;
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const double secondsPerMinute = 60;
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const double secondsPerMillisecond = 0.001;
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const unsigned fileIdentifierLength = 6;
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)bool VTTParser::parseFloatPercentageValue(VTTScanner& valueScanner, float& percentage)
5353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
5409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    float number;
5509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!valueScanner.scanFloat(number))
56a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
5709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    // '%' must be present and at the end of the setting value.
5809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!valueScanner.scan('%'))
59a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
60a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    if (number < 0 || number > 100)
61a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
62a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    percentage = number;
63a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    return true;
6453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
6553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
6609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)bool VTTParser::parseFloatPercentageValuePair(VTTScanner& valueScanner, char delimiter, FloatPoint& valuePair)
6753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
6809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    float firstCoord;
6909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!parseFloatPercentageValue(valueScanner, firstCoord))
70a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
7153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
7209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!valueScanner.scan(delimiter))
73a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
7453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
75a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    float secondCoord;
7609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!parseFloatPercentageValue(valueScanner, secondCoord))
77a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
7853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
79a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    valuePair = FloatPoint(firstCoord, secondCoord);
80a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    return true;
8153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
8253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
8351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)VTTParser::VTTParser(VTTParserClient* client, Document& document)
84bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)    : m_document(&document)
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_state(Initial)
8619cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    , m_decoder(TextResourceDecoder::create("text/plain", UTF8Encoding()))
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_currentStartTime(0)
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_currentEndTime(0)
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_client(client)
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
93f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liuvoid VTTParser::getNewCues(WillBeHeapVector<RefPtrWillBeMember<VTTCue> >& outputCues)
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
95f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    outputCues = m_cueList;
96f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    m_cueList.clear();
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
99f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liuvoid VTTParser::getNewRegions(WillBeHeapVector<RefPtrWillBeMember<VTTRegion> >& outputRegions)
10053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
10153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    outputRegions = m_regionList;
10253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    m_regionList.clear();
10353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
10453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
10551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::parseBytes(const char* data, unsigned length)
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
10719cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    String textData = m_decoder->decode(data, length);
10851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    m_lineReader.append(textData);
10951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    parse();
11051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)}
11119cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
11251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::flush()
11351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles){
11451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    String textData = m_decoder->flush();
11551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    m_lineReader.append(textData);
11651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    m_lineReader.setEndOfStream();
11751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    parse();
11851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    flushPendingCue();
11951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)}
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
12151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::parse()
12251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles){
12351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // WebVTT parser algorithm. (5.1 WebVTT file parsing.)
12451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 1 - 3 - Initial setup.
12553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
12651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    String line;
12751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    while (m_lineReader.getLine(line)) {
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        switch (m_state) {
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        case Initial:
13051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 4 - 9 - Check for a valid WebVTT signature.
13119cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)            if (!hasRequiredFileIdentifier(line)) {
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                if (m_client)
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    m_client->fileFailedToParse();
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                return;
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_state = Header;
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
13953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        case Header:
14151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 10 - 14 - Allow a header (comment area) under the WEBVTT line.
142bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)            collectMetadataHeader(line);
143bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)
14453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            if (line.isEmpty()) {
14553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                if (m_client && m_regionList.size())
14653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    m_client->newRegionsParsed();
14753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
14853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                m_state = Id;
14953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                break;
15053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            }
15153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
15251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Step 15 - Break out of header loop if the line could be a timestamp line.
15351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            if (line.contains("-->"))
15451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)                m_state = recoverCue(line);
15551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
15651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Step 16 - Line is not the empty string and does not contain "-->".
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
15853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        case Id:
16051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 17 - 20 - Allow any number of line terminators, then initialize new cue values.
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (line.isEmpty())
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                break;
16351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
16451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Step 21 - Cue creation (start a new cue).
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            resetCueValues();
16653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
16751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 22 - 25 - Check if this line contains an optional identifier or timing data.
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_state = collectCueId(line);
1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
17053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        case TimingsAndSettings:
17251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 26 - 27 - Discard current cue if the line is empty.
17319cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)            if (line.isEmpty()) {
17419cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)                m_state = Id;
17519cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)                break;
17619cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)            }
17719cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
17851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 28 - 29 - Collect cue timings and settings.
1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_state = collectTimingsAndSettings(line);
1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
18153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        case CueText:
18351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 31 - 41 - Collect the cue text, create a cue, and add it to the output.
18451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            m_state = collectCueText(line);
1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        case BadCue:
18851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            // Steps 42 - 48 - Discard lines until an empty line or a potential timing line is seen.
1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_state = ignoreBadCue(line);
1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
19551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::flushPendingCue()
19651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles){
19751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    ASSERT(m_lineReader.isAtEndOfStream());
19851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // If we're in the CueText state when we run out of data, we emit the pending cue.
19951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (m_state == CueText)
20051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        createNewCue();
20151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)}
20251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
20351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)bool VTTParser::hasRequiredFileIdentifier(const String& line)
2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // A WebVTT file identifier consists of an optional BOM character,
2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // the string "WEBVTT" followed by an optional space or tab character,
2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // and any number of characters that are not line terminators ...
20819cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    if (!line.startsWith("WEBVTT", fileIdentifierLength))
2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
21019cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    if (line.length() > fileIdentifierLength && !isASpace(line[fileIdentifierLength]))
2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return true;
2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
21651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::collectMetadataHeader(const String& line)
21753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
21851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // WebVTT header parsing (WebVTT parser algorithm step 12)
21953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    DEFINE_STATIC_LOCAL(const AtomicString, regionHeaderName, ("Region", AtomicString::ConstructFromLiteral));
22053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
22151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // The only currently supported header is the "Region" header.
22251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (!RuntimeEnabledFeatures::webVTTRegionsEnabled())
22351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        return;
22451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
22551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 12.4 If line contains the character ":" (A U+003A COLON), then set metadata's
22653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // name to the substring of line before the first ":" character and
22753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // metadata's value to the substring after this character.
22851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    size_t colonPosition = line.find(':');
22951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (colonPosition == kNotFound)
23053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        return;
23153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
23251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    String headerName = line.substring(0, colonPosition);
23353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
23451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 12.5 If metadata's name equals "Region":
23551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (headerName == regionHeaderName) {
23651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        String headerValue = line.substring(colonPosition + 1);
23751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        // Steps 12.5.1 - 12.5.11 Region creation: Let region be a new text track region [...]
23851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        createNewRegion(headerValue);
23953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    }
24053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
24153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
24251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)VTTParser::ParseState VTTParser::collectCueId(const String& line)
2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (line.contains("-->"))
2455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return collectTimingsAndSettings(line);
24609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    m_currentId = AtomicString(line);
2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return TimingsAndSettings;
2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
25051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)VTTParser::ParseState VTTParser::collectTimingsAndSettings(const String& line)
2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
25209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    VTTScanner input(line);
25309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
25451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Collect WebVTT cue timings and settings. (5.3 WebVTT cue timings and settings parsing.)
25551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 1 - 3 - Let input be the string being parsed and position be a pointer into input.
25609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    input.skipWhile<isASpace>();
2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
25851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 4 - 5 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue start time be the collected time.
25909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!collectTimeStamp(input, m_currentStartTime))
2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return BadCue;
26109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    input.skipWhile<isASpace>();
2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
26351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 6 - 9 - If the next three characters are not "-->", abort and return failure.
26409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!input.scan("-->"))
2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return BadCue;
26609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    input.skipWhile<isASpace>();
2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
26851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 10 - 11 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue end time be the collected time.
26909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!collectTimeStamp(input, m_currentEndTime))
2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return BadCue;
27109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    input.skipWhile<isASpace>();
2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
27351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 12 - Parse the WebVTT settings for the cue (conducted in TextTrackCue).
27409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    m_currentSettings = input.restOfInputAsString();
2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return CueText;
2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
27851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)VTTParser::ParseState VTTParser::collectCueText(const String& line)
2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
28051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 34.
2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (line.isEmpty()) {
2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        createNewCue();
2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return Id;
2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
28551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 35.
28651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (line.contains("-->")) {
28751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        // Step 39-40.
28851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        createNewCue();
28951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
29051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        // Step 41 - New iteration of the cue loop.
29151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        return recoverCue(line);
29251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    }
2935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_currentContent.isEmpty())
2949e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)        m_currentContent.append('\n');
2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentContent.append(line);
2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return CueText;
2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
30051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)VTTParser::ParseState VTTParser::recoverCue(const String& line)
3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
30251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 17 and 21.
30351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    resetCueValues();
30451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
30551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 22.
30651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    return collectTimingsAndSettings(line);
30751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)}
30851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
30951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)VTTParser::ParseState VTTParser::ignoreBadCue(const String& line)
31051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles){
31151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (line.isEmpty())
31251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        return Id;
31351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (line.contains("-->"))
31451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        return recoverCue(line);
31551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    return BadCue;
3165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
31819cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)// A helper class for the construction of a "cue fragment" from the cue text.
31951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)class VTTTreeBuilder {
320f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    STACK_ALLOCATED();
32119cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)public:
322f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    explicit VTTTreeBuilder(Document& document)
323f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        : m_document(&document) { }
32419cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
325d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)    PassRefPtrWillBeRawPtr<DocumentFragment> buildFromString(const String& cueText);
32619cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
32719cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)private:
32819cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    void constructTreeFromToken(Document&);
329f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    Document& document() const { return *m_document; }
33019cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
33151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    VTTToken m_token;
332f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    RefPtrWillBeMember<ContainerNode> m_currentNode;
33319cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    Vector<AtomicString> m_languageStack;
334f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    RawPtrWillBeMember<Document> m_document;
33519cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)};
33619cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
337d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)PassRefPtrWillBeRawPtr<DocumentFragment> VTTTreeBuilder::buildFromString(const String& cueText)
3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // Cue text processing based on
34051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // 5.4 WebVTT cue text parsing rules, and
34151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // 5.5 WebVTT cue text DOM construction rules
3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
343f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(document());
3443c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch
34519cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    if (cueText.isEmpty()) {
346f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        fragment->parserAppendChild(Text::create(document(), ""));
3473c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        return fragment;
3483c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch    }
3493c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch
3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentNode = fragment;
35102772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch
35251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    VTTTokenizer tokenizer(cueText);
353926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    m_languageStack.clear();
35419cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
35551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    while (tokenizer.nextToken(m_token))
356f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        constructTreeFromToken(document());
35702772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch
3585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return fragment.release();
3595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
361d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)PassRefPtrWillBeRawPtr<DocumentFragment> VTTParser::createDocumentFragmentFromCueText(Document& document, const String& cueText)
36219cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles){
36351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    VTTTreeBuilder treeBuilder(document);
36419cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    return treeBuilder.buildFromString(cueText);
36519cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)}
36619cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)
36751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::createNewCue()
3685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
369f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    RefPtrWillBeRawPtr<VTTCue> cue = VTTCue::create(*m_document, m_currentStartTime, m_currentEndTime, m_currentContent.toString());
3705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    cue->setId(m_currentId);
37151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    cue->parseSettings(m_currentSettings);
3725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
373f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    m_cueList.append(cue);
3745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_client)
3755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_client->newCuesParsed();
3765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
37851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::resetCueValues()
3795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
38009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    m_currentId = emptyAtom;
3815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentSettings = emptyString();
3825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentStartTime = 0;
3835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentEndTime = 0;
3845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentContent.clear();
3855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
38751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTParser::createNewRegion(const String& headerValue)
38853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
38951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    if (headerValue.isEmpty())
39053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        return;
39153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
39251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 12.5.1 - 12.5.9 - Construct and initialize a WebVTT Region object.
393f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    RefPtrWillBeRawPtr<VTTRegion> region = VTTRegion::create();
39451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    region->setRegionSettings(headerValue);
39553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
39651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 12.5.10 If the text track list of regions regions contains a region
39753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // with the same region identifier value as region, remove that region.
39851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    for (size_t i = 0; i < m_regionList.size(); ++i) {
39953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        if (m_regionList[i]->id() == region->id()) {
40053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            m_regionList.remove(i);
40153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            break;
40253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        }
40351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    }
40453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
40551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 12.5.11
40653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    m_regionList.append(region);
40753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
40853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
40909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)bool VTTParser::collectTimeStamp(const String& line, double& timeStamp)
41009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){
41109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    VTTScanner input(line);
41209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    return collectTimeStamp(input, timeStamp);
41309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)}
41409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)
41509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)bool VTTParser::collectTimeStamp(VTTScanner& input, double& timeStamp)
4165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
41751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Collect a WebVTT timestamp (5.3 WebVTT cue timings and settings parsing.)
41851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 1 - 4 - Initial checks, let most significant units be minutes.
41951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    enum Mode { Minutes, Hours };
42051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    Mode mode = Minutes;
4215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
422a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    // Steps 5 - 7 - Collect a sequence of characters that are 0-9.
423a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    // If not 2 characters or value is greater than 59, interpret as hours.
424a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    int value1;
42509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    unsigned value1Digits = input.scanDigits(value1);
426a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    if (!value1Digits)
427a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
428a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    if (value1Digits != 2 || value1 > 59)
42951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        mode = Hours;
4305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
43151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 8 - 11 - Collect the next sequence of 0-9 after ':' (must be 2 chars).
432a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    int value2;
43309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!input.scan(':') || input.scanDigits(value2) != 2)
434a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
4355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
43651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Step 12 - Detect whether this timestamp includes hours.
4375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int value3;
43809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (mode == Hours || input.match(':')) {
43909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        if (!input.scan(':') || input.scanDigits(value3) != 2)
440a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            return false;
4415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
4425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        value3 = value2;
4435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        value2 = value1;
4445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        value1 = 0;
4455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
4465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
44751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 13 - 17 - Collect next sequence of 0-9 after '.' (must be 3 chars).
448a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    int value4;
44909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!input.scan('.') || input.scanDigits(value4) != 3)
450a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
4515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (value2 > 59 || value3 > 59)
452a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        return false;
4535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
45451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    // Steps 18 - 19 - Calculate result.
455a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    timeStamp = (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + (value4 * secondsPerMillisecond);
456a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    return true;
4575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
4585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
45951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)static VTTNodeType tokenToNodeType(VTTToken& token)
460926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles){
46151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    switch (token.name().length()) {
462926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    case 1:
463926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'c')
46451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeClass;
465926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'v')
46651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeVoice;
467926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'b')
46851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeBold;
469926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'i')
47051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeItalic;
471926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'u')
47251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeUnderline;
473926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        break;
474926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    case 2:
475926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'r' && token.name()[1] == 't')
47651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeRubyText;
477926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        break;
478926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    case 4:
479926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'r' && token.name()[1] == 'u' && token.name()[2] == 'b' && token.name()[3] == 'y')
48051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeRuby;
481926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        if (token.name()[0] == 'l' && token.name()[1] == 'a' && token.name()[2] == 'n' && token.name()[3] == 'g')
48251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)            return VTTNodeTypeLanguage;
483926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        break;
484926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    }
48551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    return VTTNodeTypeNone;
486926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)}
487926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)
48851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)void VTTTreeBuilder::constructTreeFromToken(Document& document)
4895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
4905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules
491926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)
4925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    switch (m_token.type()) {
49351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    case VTTTokenTypes::Character: {
494d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)        m_currentNode->parserAppendChild(Text::create(document, m_token.characters()));
4955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
4965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
49751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    case VTTTokenTypes::StartTag: {
49851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        VTTNodeType nodeType = tokenToNodeType(m_token);
499a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (nodeType == VTTNodeTypeNone)
500a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            break;
501a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)
502a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        VTTNodeType currentType = m_currentNode->isVTTElement() ? toVTTElement(m_currentNode.get())->webVTTNodeType() : VTTNodeTypeNone;
503a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        // <rt> is only allowed if the current node is <ruby>.
504a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (nodeType == VTTNodeTypeRubyText && currentType != VTTNodeTypeRuby)
505a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            break;
506a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)
507323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)        RefPtrWillBeRawPtr<VTTElement> child = VTTElement::create(nodeType, &document);
508a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (!m_token.classes().isEmpty())
509a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            child->setAttribute(classAttr, m_token.classes());
510a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)
511a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (nodeType == VTTNodeTypeVoice) {
512a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            child->setAttribute(VTTElement::voiceAttributeName(), m_token.annotation());
513a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        } else if (nodeType == VTTNodeTypeLanguage) {
514a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            m_languageStack.append(m_token.annotation());
515a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            child->setAttribute(VTTElement::langAttributeName(), m_languageStack.last());
5165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
517a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (!m_languageStack.isEmpty())
518a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            child->setLanguage(m_languageStack.last());
519a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        m_currentNode->parserAppendChild(child);
520a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        m_currentNode = child;
5215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
5225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
52351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    case VTTTokenTypes::EndTag: {
52451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        VTTNodeType nodeType = tokenToNodeType(m_token);
525a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (nodeType == VTTNodeTypeNone)
526a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            break;
527a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)
528a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        // The only non-VTTElement would be the DocumentFragment root. (Text
529a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        // nodes and PIs will never appear as m_currentNode.)
530a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (!m_currentNode->isVTTElement())
531a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            break;
532a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)
533a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        VTTNodeType currentType = toVTTElement(m_currentNode.get())->webVTTNodeType();
534a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        bool matchesCurrent = nodeType == currentType;
535a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (!matchesCurrent) {
536a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            // </ruby> auto-closes <rt>.
537a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            if (currentType == VTTNodeTypeRubyText && nodeType == VTTNodeTypeRuby) {
538a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)                if (m_currentNode->parentNode())
539a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)                    m_currentNode = m_currentNode->parentNode();
540a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            } else {
541a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)                break;
542a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            }
5435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
544a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (nodeType == VTTNodeTypeLanguage)
545a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            m_languageStack.removeLast();
546a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        if (m_currentNode->parentNode())
547a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)            m_currentNode = m_currentNode->parentNode();
5485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
549926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    }
55051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    case VTTTokenTypes::TimestampTag: {
55151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        String charactersString = m_token.characters();
552a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)        double parsedTimeStamp;
55309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        if (VTTParser::collectTimeStamp(charactersString, parsedTimeStamp))
554926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)            m_currentNode->parserAppendChild(ProcessingInstruction::create(document, "timestamp", charactersString));
5555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
5565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
5575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    default:
5585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
5595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
5605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
5615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
562f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liuvoid VTTParser::trace(Visitor* visitor)
563f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu{
5645d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    visitor->trace(m_document);
565f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    visitor->trace(m_cueList);
566f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu    visitor->trace(m_regionList);
5675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
5685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
569f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu}
570