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