Add a flushing mechanism for the WebVTTParser
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Mar 2014 20:59:53 +0000 (20:59 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Mar 2014 20:59:53 +0000 (20:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=130532

Reviewed by Eric Carlson.

Merged from Blink (patch by vcarbune@chromium.org):
https://chromium.googlesource.com/chromium/blink/+/1ac2b3081492ab8661429230004013a9a3623f0f
http://crbug.com/54203003
https://chromium.googlesource.com/chromium/blink/+/24343d32bc36d20d59ef3e10040faddde65929e6
http://crbug.com/59453002
https://chromium.googlesource.com/chromium/blink/+/ef2793f1d9d207389589ecf9a136ea5c7170b4af
http://crbug.com/75233002
https://chromium.googlesource.com/chromium/blink/+/a29f2f8324abcd4826b41e7dc34373dee2ec57a9
http://crbug.com/52713005
https://chromium.googlesource.com/chromium/blink/+/7ce003c8d47b39f116f1f2a592a5dfb4a9e77228
http://crbug.com/64303004
https://chromium.googlesource.com/chromium/blink/+/18f896b3498478311e880f782813d5dfc8c0c7d1
http://crbug.com/96933004
https://chromium.googlesource.com/chromium/blink/+/8670e8ecb13254a651f5493ec83f4484d18c154e
http://crbug.com/98783005
https://chromium.googlesource.com/chromium/blink/+/4ac55780a6af3d76e0159c1d145330964ad56647
http://crbug.com/104713002

Tests: http/tests/media/track/track-webvtt-slow-loading-2.html
       http/tests/media/track/track-webvtt-slow-loading.html

* CMakeLists.txt:
* GNUmakefile.list.am:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:
* html/track/BufferedLineReader.cpp: Added.
* html/track/BufferedLineReader.h: Added.
* html/track/TextTrackRegion.cpp:
* html/track/VTTCue.cpp:
* html/track/WebVTTParser.cpp:
* html/track/WebVTTParser.h:
* loader/TextTrackLoader.cpp:

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

13 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/html/track/BufferedLineReader.cpp [new file with mode: 0644]
Source/WebCore/html/track/BufferedLineReader.h [new file with mode: 0644]
Source/WebCore/html/track/TextTrackRegion.cpp
Source/WebCore/html/track/VTTCue.cpp
Source/WebCore/html/track/WebVTTParser.cpp
Source/WebCore/html/track/WebVTTParser.h
Source/WebCore/loader/TextTrackLoader.cpp

index 4175294..0c4f5e0 100644 (file)
@@ -2725,6 +2725,7 @@ if (ENABLE_VIDEO_TRACK)
     list(APPEND WebCore_SOURCES
         html/track/AudioTrack.cpp
         html/track/AudioTrackList.cpp
+        html/track/BufferedLineReader.cpp
         html/track/DataCue.cpp
         html/track/InbandGenericTextTrack.cpp
         html/track/InbandTextTrack.cpp
index 6a1d41b..8bd82c1 100644 (file)
@@ -1,3 +1,44 @@
+2014-03-20  Brent Fulgham  <bfulgham@apple.com>
+
+        Add a flushing mechanism for the WebVTTParser
+        https://bugs.webkit.org/show_bug.cgi?id=130532
+
+        Reviewed by Eric Carlson.
+
+        Merged from Blink (patch by vcarbune@chromium.org):
+        https://chromium.googlesource.com/chromium/blink/+/1ac2b3081492ab8661429230004013a9a3623f0f
+        http://crbug.com/54203003
+        https://chromium.googlesource.com/chromium/blink/+/24343d32bc36d20d59ef3e10040faddde65929e6
+        http://crbug.com/59453002
+        https://chromium.googlesource.com/chromium/blink/+/ef2793f1d9d207389589ecf9a136ea5c7170b4af
+        http://crbug.com/75233002
+        https://chromium.googlesource.com/chromium/blink/+/a29f2f8324abcd4826b41e7dc34373dee2ec57a9
+        http://crbug.com/52713005
+        https://chromium.googlesource.com/chromium/blink/+/7ce003c8d47b39f116f1f2a592a5dfb4a9e77228
+        http://crbug.com/64303004
+        https://chromium.googlesource.com/chromium/blink/+/18f896b3498478311e880f782813d5dfc8c0c7d1
+        http://crbug.com/96933004
+        https://chromium.googlesource.com/chromium/blink/+/8670e8ecb13254a651f5493ec83f4484d18c154e
+        http://crbug.com/98783005
+        https://chromium.googlesource.com/chromium/blink/+/4ac55780a6af3d76e0159c1d145330964ad56647
+        http://crbug.com/104713002
+
+        Tests: http/tests/media/track/track-webvtt-slow-loading-2.html
+               http/tests/media/track/track-webvtt-slow-loading.html
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.vcxproj/WebCore.vcxproj.filters:
+        * WebCore.xcodeproj/project.pbxproj:
+        * html/track/BufferedLineReader.cpp: Added.
+        * html/track/BufferedLineReader.h: Added.
+        * html/track/TextTrackRegion.cpp:
+        * html/track/VTTCue.cpp:
+        * html/track/WebVTTParser.cpp:
+        * html/track/WebVTTParser.h:
+        * loader/TextTrackLoader.cpp:
+
 2014-03-20  Alex Christensen  <achristensen@webkit.org>
 
         Preparation for using Soup on Windows.
index 941a3d2..5e0ad7a 100644 (file)
@@ -3682,6 +3682,8 @@ webcore_sources += \
        Source/WebCore/html/track/AudioTrack.h \
        Source/WebCore/html/track/AudioTrackList.cpp \
        Source/WebCore/html/track/AudioTrackList.h \
+       Source/WebCore/html/track/BufferedLineReader.cpp \
+       Source/WebCore/html/track/BufferedLineReader.h \
        Source/WebCore/html/track/DataCue.cpp \
        Source/WebCore/html/track/DataCue.h \
        Source/WebCore/html/track/InbandGenericTextTrack.cpp \
index e1591fc..459757a 100644 (file)
     <ClCompile Include="..\html\shadow\SliderThumbElement.cpp" />
     <ClCompile Include="..\html\shadow\SpinButtonElement.cpp" />
     <ClCompile Include="..\html\shadow\TextControlInnerElements.cpp" />
+    <ClCompile Include="..\html\track\BufferedLineReader.cpp" />
     <ClCompile Include="..\html\track\DataCue.cpp" />
     <ClCompile Include="..\html\track\InbandGenericTextTrack.cpp" />
     <ClCompile Include="..\html\track\InbandTextTrack.cpp" />
     <ClInclude Include="..\html\shadow\SliderThumbElement.h" />
     <ClInclude Include="..\html\shadow\SpinButtonElement.h" />
     <ClInclude Include="..\html\shadow\TextControlInnerElements.h" />
+    <ClInclude Include="..\html\track\BufferedLineReader.h" />
     <ClInclude Include="..\html\track\DataCue.h" />
     <ClInclude Include="..\html\track\InbandGenericTextTrack.h" />
     <ClInclude Include="..\html\track\InbandTextTrack.h" />
index 0e5ef7d..0ba1858 100644 (file)
     <ClCompile Include="..\html\shadow\TextControlInnerElements.cpp">
       <Filter>html\shadow</Filter>
     </ClCompile>
+    <ClCompile Include="..\html\track\BufferedLineReader.cpp">
+      <Filter>html\track</Filter>
+    </ClCompile>
     <ClCompile Include="..\html\track\DataCue.cpp">
       <Filter>html\track</Filter>
     </ClCompile>
     <ClInclude Include="..\html\shadow\TextControlInnerElements.h">
       <Filter>html\shadow</Filter>
     </ClInclude>
+    <ClInclude Include="..\html\track\BufferedLineReader.h">
+      <Filter>html\track</Filter>
+    </ClInclude>
     <ClInclude Include="..\html\track\DataCue.h">
       <Filter>html\track</Filter>
     </ClInclude>
index c7654b9..7f97343 100644 (file)
                7A24587C1021EAF4000A00AA /* InspectorDOMAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A24587A1021EAF4000A00AA /* InspectorDOMAgent.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7A29BA6A187B7C1D00F29CEB /* TemporaryOpenGLSetting.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A29BA69187B781C00F29CEB /* TemporaryOpenGLSetting.cpp */; };
                7A29F57218C69514004D0F81 /* OutOfBandTextTrackPrivateAVF.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A29F57118C69514004D0F81 /* OutOfBandTextTrackPrivateAVF.h */; };
+               7A45032F18DB717200377B34 /* BufferedLineReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A45032D18DB717200377B34 /* BufferedLineReader.cpp */; };
+               7A45033018DB717200377B34 /* BufferedLineReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A45032E18DB717200377B34 /* BufferedLineReader.h */; };
                7A54857F14E02D51006AE05A /* InspectorHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A54857D14E02D51006AE05A /* InspectorHistory.cpp */; };
                7A54858014E02D51006AE05A /* InspectorHistory.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A54857E14E02D51006AE05A /* InspectorHistory.h */; };
                7A54881714E432A1006AE05A /* DOMPatchSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A54881514E432A1006AE05A /* DOMPatchSupport.h */; };
                7A29BA67187B732200F29CEB /* TemporaryOpenGLSetting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemporaryOpenGLSetting.h; sourceTree = "<group>"; };
                7A29BA69187B781C00F29CEB /* TemporaryOpenGLSetting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TemporaryOpenGLSetting.cpp; sourceTree = "<group>"; };
                7A29F57118C69514004D0F81 /* OutOfBandTextTrackPrivateAVF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OutOfBandTextTrackPrivateAVF.h; path = objc/OutOfBandTextTrackPrivateAVF.h; sourceTree = "<group>"; };
+               7A45032D18DB717200377B34 /* BufferedLineReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BufferedLineReader.cpp; sourceTree = "<group>"; };
+               7A45032E18DB717200377B34 /* BufferedLineReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BufferedLineReader.h; sourceTree = "<group>"; };
                7A54857D14E02D51006AE05A /* InspectorHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorHistory.cpp; sourceTree = "<group>"; };
                7A54857E14E02D51006AE05A /* InspectorHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorHistory.h; sourceTree = "<group>"; };
                7A54881514E432A1006AE05A /* DOMPatchSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMPatchSupport.h; sourceTree = "<group>"; };
                B1AD4E7713A12A7200846B27 /* track */ = {
                        isa = PBXGroup;
                        children = (
+                               7A45032D18DB717200377B34 /* BufferedLineReader.cpp */,
+                               7A45032E18DB717200377B34 /* BufferedLineReader.h */,
                                BE88E0CC1715D2A200658D98 /* AudioTrack.cpp */,
                                BE88E0CD1715D2A200658D98 /* AudioTrack.h */,
                                BE88E0CE1715D2A200658D98 /* AudioTrack.idl */,
                                A80E6D0C0A1989CA007FB8C5 /* CSSStyleRule.h in Headers */,
                                A8EA80070A19516E00A8EF5F /* CSSStyleSheet.h in Headers */,
                                FC54D05716A7673100575E4D /* CSSSupportsRule.h in Headers */,
+                               7A45033018DB717200377B34 /* BufferedLineReader.h in Headers */,
                                BC80C9880CD294EE00A0B7B3 /* CSSTimingFunctionValue.h in Headers */,
                                A882DA231593848D000115ED /* CSSToStyleMap.h in Headers */,
                                371F53E90D2704F900ECE0D5 /* CSSUnicodeRangeValue.h in Headers */,
                                B2FA3DCE0AB75A6F000E5AC4 /* JSSVGPathSegLinetoAbs.cpp in Sources */,
                                B2FA3DD00AB75A6F000E5AC4 /* JSSVGPathSegLinetoHorizontalAbs.cpp in Sources */,
                                B2FA3DD20AB75A6F000E5AC4 /* JSSVGPathSegLinetoHorizontalRel.cpp in Sources */,
+                               7A45032F18DB717200377B34 /* BufferedLineReader.cpp in Sources */,
                                B2FA3DD40AB75A6F000E5AC4 /* JSSVGPathSegLinetoRel.cpp in Sources */,
                                B2FA3DD60AB75A6F000E5AC4 /* JSSVGPathSegLinetoVerticalAbs.cpp in Sources */,
                                B2FA3DD80AB75A6F000E5AC4 /* JSSVGPathSegLinetoVerticalRel.cpp in Sources */,
diff --git a/Source/WebCore/html/track/BufferedLineReader.cpp b/Source/WebCore/html/track/BufferedLineReader.cpp
new file mode 100644 (file)
index 0000000..c23924b
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013, Opera Software ASA. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "BufferedLineReader.h"
+
+#include <wtf/unicode/CharacterNames.h>
+
+namespace WebCore {
+
+bool BufferedLineReader::getLine(String& line)
+{
+    if (m_maybeSkipLF) {
+        // We ran out of data after a CR (U+000D), which means that we may be
+        // in the middle of a CRLF pair. If the next character is a LF (U+000A)
+        // then skip it, and then (unconditionally) return the buffered line.
+        if (!m_buffer.isEmpty()) {
+            scanCharacter(newlineCharacter);
+            m_maybeSkipLF = false;
+        }
+        // If there was no (new) data available, then keep m_maybeSkipLF set,
+        // and fall through all the way down to the EOS check at the end of
+        // the method.
+    }
+
+    bool shouldReturnLine = false;
+    bool checkForLF = false;
+    while (!m_buffer.isEmpty()) {
+        UChar c = m_buffer.currentChar();
+        m_buffer.advance();
+
+        if (c == newlineCharacter || c == carriageReturn) {
+            // We found a line ending. Return the accumulated line.
+            shouldReturnLine = true;
+            checkForLF = (c == carriageReturn);
+            break;
+        }
+
+        // NULs are transformed into U+FFFD (REPLACEMENT CHAR.) in step 1 of
+        // the WebVTT parser algorithm.
+        if (c == '\0')
+            c = replacementCharacter;
+
+        m_lineBuffer.append(c);
+    }
+
+    if (checkForLF) {
+        // May be in the middle of a CRLF pair.
+        if (!m_buffer.isEmpty()) {
+            // Scan a potential newline character.
+            scanCharacter(newlineCharacter);
+        } else {
+            // Check for the LF on the next call (unless we reached EOS, in
+            // which case we'll return the contents of the line buffer, and
+            // reset state for the next line.)
+            m_maybeSkipLF = true;
+        }
+    }
+
+    if (isAtEndOfStream()) {
+        // We've reached the end of the stream proper. Emit a line if the
+        // current line buffer is non-empty. (Note that if shouldReturnLine is
+        // set already, we want to return a line nonetheless.)
+        shouldReturnLine |= !m_lineBuffer.isEmpty();
+    }
+
+    if (shouldReturnLine) {
+        line = m_lineBuffer.toString();
+        m_lineBuffer.clear();
+        return true;
+    }
+
+    ASSERT(m_buffer.isEmpty());
+    return false;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/html/track/BufferedLineReader.h b/Source/WebCore/html/track/BufferedLineReader.h
new file mode 100644 (file)
index 0000000..df3a749
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013, Opera Software ASA. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BufferedLineReader_h
+#define BufferedLineReader_h
+
+#include "SegmentedString.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+// Line collection helper for the WebVTT Parser.
+//
+// Converts a stream of data (== a sequence of Strings) into a set of
+// lines. CR, LR or CRLF are considered linebreaks. Normalizes NULs (U+0000)
+// to 'REPLACEMENT CHARACTER' (U+FFFD) and does not return the linebreaks as
+// part of the result.
+class BufferedLineReader {
+    WTF_MAKE_NONCOPYABLE(BufferedLineReader);
+public:
+    BufferedLineReader()
+        : m_endOfStream(false)
+        , m_maybeSkipLF(false) { }
+
+    // Append data to the internal buffer.
+    void append(const String& data)
+    {
+        ASSERT(!m_endOfStream);
+        m_buffer.append(SegmentedString(data));
+    }
+
+    // Indicate that no more data will be appended. This will cause any
+    // potentially "unterminated" line to be returned from getLine.
+    void setEndOfStream() { m_endOfStream = true; }
+
+    // Attempt to read a line from the internal buffer (fed via append).
+    // If successful, true is returned and |line| is set to the line that was
+    // read. If no line could be read false is returned.
+    bool getLine(String& line);
+
+    // Returns true if EOS has been reached proper.
+    bool isAtEndOfStream() const { return m_endOfStream && m_buffer.isEmpty(); }
+
+private:
+    // Consume the next character the buffer if it is the character |c|.
+    void scanCharacter(UChar c)
+    {
+        ASSERT(!m_buffer.isEmpty());
+        if (m_buffer.currentChar() == c)
+            m_buffer.advance();
+    }
+
+    SegmentedString m_buffer;
+    StringBuilder m_lineBuffer;
+    bool m_endOfStream;
+    bool m_maybeSkipLF;
+};
+
+} // namespace WebCore
+
+#endif
index 655da72..f81f30c 100644 (file)
@@ -262,25 +262,22 @@ void TextTrackRegion::parseSettingValue(RegionSetting setting, const String& val
 {
     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, scrollUpValueKeyword, ("up", AtomicString::ConstructFromLiteral));
 
-    bool isValidSetting;
-    String numberAsString;
-    int number;
-    FloatPoint anchorPosition;
-
     switch (setting) {
     case Id:
         if (value.find("-->") == notFound)
             m_id = value;
         break;
-    case Width:
-        number = WebVTTParser::parseFloatPercentageValue(value, isValidSetting);
-        if (isValidSetting)
-            m_width = number;
+    case Width: {
+        float floatWidth;
+        if (WebVTTParser::parseFloatPercentageValue(value, floatWidth))
+            m_width = floatWidth;
         else
             LOG(Media, "TextTrackRegion::parseSettingValue, invalid Width");
         break;
+    }
     case Height: {
         unsigned position = 0;
+        int number;
         if (WebVTTParser::collectDigitsToInt(value, &position, number) && position == value.length())
             m_heightInLines = number;
         else
@@ -288,17 +285,11 @@ void TextTrackRegion::parseSettingValue(RegionSetting setting, const String& val
         break;
     }
     case RegionAnchor:
-        anchorPosition = WebVTTParser::parseFloatPercentageValuePair(value, ',', isValidSetting);
-        if (isValidSetting)
-            m_regionAnchor = anchorPosition;
-        else
+        if (!WebVTTParser::parseFloatPercentageValuePair(value, ',', m_regionAnchor))
             LOG(Media, "TextTrackRegion::parseSettingValue, invalid RegionAnchor");
         break;
     case ViewportAnchor:
-        anchorPosition = WebVTTParser::parseFloatPercentageValuePair(value, ',', isValidSetting);
-        if (isValidSetting)
-            m_viewportAnchor = anchorPosition;
-        else
+        if (!WebVTTParser::parseFloatPercentageValuePair(value, ',', m_viewportAnchor))
             LOG(Media, "TextTrackRegion::parseSettingValue, invalid ViewportAnchor");
         break;
     case Scroll:
index 4ce9e77..c0fd716 100644 (file)
@@ -427,7 +427,7 @@ void VTTCue::setText(const String& text)
 void VTTCue::createWebVTTNodeTree()
 {
     if (!m_webVTTNodeTree)
-        m_webVTTNodeTree = std::make_unique<WebVTTParser>(nullptr, scriptExecutionContext())->createDocumentFragmentFromCueText(m_content);
+        m_webVTTNodeTree = WebVTTParser::createDocumentFragmentFromCueText(ownerDocument(), m_content);
 }
 
 void VTTCue::copyWebVTTNodeToDOMTree(ContainerNode* webVTTNode, ContainerNode* parent)
@@ -696,8 +696,9 @@ void VTTCue::markFutureAndPastNodes(ContainerNode* root, double previousTimestam
         if (child->nodeName() == timestampTag) {
             unsigned position = 0;
             String timestamp = child->nodeValue();
-            double currentTimestamp = WebVTTParser::collectTimeStamp(timestamp, &position);
-            ASSERT(currentTimestamp != -1);
+            double currentTimestamp;
+            bool check = WebVTTParser::collectTimeStamp(timestamp, position, currentTimestamp);
+            ASSERT_UNUSED(check, check);
             
             if (currentTimestamp > movieTime)
                 isPastNode = false;
@@ -970,7 +971,7 @@ void VTTCue::setCueSettings(const String& input)
             // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
             //    then jump to the step labeled next setting.
             int number;
-            if (!WebVTTParser::collectDigitsToInt(input, &position, number))
+            if (!WebVTTParser::collectDigitsToInt(input, position, number))
                 break;
             if (position >= input.length())
                 break;
@@ -986,7 +987,6 @@ void VTTCue::setCueSettings(const String& input)
 
             // 5. Ignoring the trailing percent sign, interpret value as an integer, and let number be that number.
             // 6. If number is not in the range 0 ≤ number ≤ 100, then jump to the step labeled next setting.
-            // NOTE: toInt ignores trailing non-digit characters, such as '%'.
             if (number < 0 || number > 100)
                 break;
 
@@ -1001,7 +1001,7 @@ void VTTCue::setCueSettings(const String& input)
             // 2. If value does not contain at least one character in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT 
             //    NINE (9), then jump to the step labeled next setting.
             int number;
-            if (!WebVTTParser::collectDigitsToInt(input, &position, number))
+            if (!WebVTTParser::collectDigitsToInt(input, position, number))
                 break;
             if (position >= input.length())
                 break;
index c018b5d..e80fbfe 100644 (file)
@@ -45,22 +45,20 @@ namespace WebCore {
 const double secondsPerHour = 3600;
 const double secondsPerMinute = 60;
 const double secondsPerMillisecond = 0.001;
-const double malformedTime = -1;
-const UChar bom = 0xFEFF;
 const char* fileIdentifier = "WEBVTT";
 const unsigned fileIdentifierLength = 6;
 
-static unsigned scanDigits(const String& input, unsigned* position)
+static unsigned scanDigits(const String& input, unsigned& position)
 {
-    unsigned startPosition = *position;
-    while (*position < input.length() && isASCIIDigit(input[*position]))
-        (*position)++;
-    return *position - startPosition;
+    unsigned startPosition = position;
+    while (position < input.length() && isASCIIDigit(input[position]))
+        position++;
+    return position - startPosition;
 }
     
-unsigned WebVTTParser::collectDigitsToInt(const String& input, unsigned* position, int& number)
+unsigned WebVTTParser::collectDigitsToInt(const String& input, unsigned& position, int& number)
 {
-    unsigned startPosition = *position;
+    unsigned startPosition = position;
     unsigned numDigits = scanDigits(input, position);
     if (!numDigits) {
         number = 0;
@@ -91,13 +89,11 @@ String WebVTTParser::collectWord(const String& input, unsigned* position)
 }
 
 #if ENABLE(WEBVTT_REGIONS)
-float WebVTTParser::parseFloatPercentageValue(const String& value, bool& isValidSetting)
+bool WebVTTParser::parseFloatPercentageValue(const String& value, float& percentage)
 {
     // '%' must be present and at the end of the setting value.
-    if (value.find('%', 1) != value.length() - 1) {
-        isValidSetting = false;
-        return 0;
-    }
+    if (value.isEmpty() || value[value.length() - 1] != '%')
+        return false;
 
     unsigned position = 0;
     unsigned digitsBeforeDot = scanDigits(value, &position);
@@ -109,44 +105,45 @@ float WebVTTParser::parseFloatPercentageValue(const String& value, bool& isValid
     }
 
     // At least one digit is required
-    if (!digitsBeforeDot && !digitsAfterDot) {
-        isValidSetting = false;
-        return 0;
-    }
+    if (!digitsBeforeDot && !digitsAfterDot)
+        return false;
 
     float number = value.toFloat();
-    isValidSetting = number >= 0 && number <= 100;
-    return number;
+    if (number < 0 || number > 100)
+        return false;
+
+    percentage = number;
+    return true;
 }
 
-FloatPoint WebVTTParser::parseFloatPercentageValuePair(const String& value, char delimiter, bool& isValidSetting)
+bool WebVTTParser::parseFloatPercentageValuePair(const String& value, char delimiter, FloatPoint& valuePair)
 {
     // The delimiter can't be the first or second value because a pair of
     // percentages (x%,y%) implies that at least the first two characters
     // are the first percentage value.
     size_t delimiterOffset = value.find(delimiter, 2);
-    if (delimiterOffset == notFound || delimiterOffset == value.length() - 1) {
-        isValidSetting = false;
-        return FloatPoint(0, 0);
-    }
+    if (delimiterOffset == notFound || delimiterOffset == value.length() - 1)
+        return false;
 
-    bool isFirstValueValid;
-    float firstCoord = parseFloatPercentageValue(value.substring(0, delimiterOffset), isFirstValueValid);
+    float firstCoord;
+    if (!parseFloatPercentageValue(value.substring(0, delimiterOffset), firstCoord))
+        return false;
 
-    bool isSecondValueValid;
-    float secondCoord = parseFloatPercentageValue(value.substring(delimiterOffset + 1, value.length() - 1), isSecondValueValid);
+    float secondCoord;
+    if (!parseFloatPercentageValue(value.substring(delimiterOffset + 1, value.length() - 1), secondCoord))
+        return false;
 
-    isValidSetting = isFirstValueValid && isSecondValueValid;
-    return FloatPoint(firstCoord, secondCoord);
+    valuePair = FloatPoint(firstCoord, secondCoord);
+    return true;
 }
 #endif
 
 WebVTTParser::WebVTTParser(WebVTTParserClient* client, ScriptExecutionContext* context)
     : m_scriptExecutionContext(context)
     , m_state(Initial)
+    , m_decoder(TextResourceDecoder::create("text/plain", UTF8Encoding()))
     , m_currentStartTime(0)
     , m_currentEndTime(0)
-    , m_tokenizer(std::make_unique<WebVTTTokenizer>())
     , m_client(client)
 {
 }
@@ -167,16 +164,28 @@ void WebVTTParser::getNewRegions(Vector<RefPtr<TextTrackRegion>>& outputRegions)
 
 void WebVTTParser::parseBytes(const char* data, unsigned length)
 {
+    String textData = m_decoder->decode(data, length);
+    m_lineReader.append(textData);
+    parse();
+}
+
+void WebVTTParser::flush()
+{
+    String textData = m_decoder->flush();
+    m_lineReader.append(textData);
+    m_lineReader.setEndOfStream();
+    parse();
+    flushPendingCue();
+}
+
+void WebVTTParser::parse()
+{    
     // WebVTT parser algorithm. (5.1 WebVTT file parsing.)
     // Steps 1 - 3 - Initial setup.
-    unsigned position = 0;
-
-    while (position < length) {
-        String line = collectNextLine(data, length, &position);
-        if (line.isNull()) {
-            m_buffer.append(data + position, length - position);
+    String line;
+    while (m_lineReader.getLine(line)) {
+        if (line.isNull())
             return;
-        }
 
         switch (m_state) {
         case Initial:
@@ -206,7 +215,7 @@ void WebVTTParser::parseBytes(const char* data, unsigned length)
             break;
 
         case Id:
-            // Step 17-20 - Allow any number of line terminators, then initialize new cue values.
+            // Steps 17 - 20 - Allow any number of line terminators, then initialize new cue values.
             if (line.isEmpty())
                 break;
 
@@ -252,28 +261,26 @@ void WebVTTParser::fileFinished()
     m_state = Finished;
 }
 
+void WebVTTParser::flushPendingCue()
+{
+    ASSERT(m_lineReader.isAtEndOfStream());
+    // If we're in the CueText state when we run out of data, we emit the pending cue.
+    if (m_state == CueText)
+        createNewCue();
+}
+
 bool WebVTTParser::hasRequiredFileIdentifier(const String& line)
 {
     // A WebVTT file identifier consists of an optional BOM character,
     // the string "WEBVTT" followed by an optional space or tab character,
     // and any number of characters that are not line terminators ...
-    unsigned linePos = 0;
-
     if (line.isEmpty())
         return false;
 
-    if (line[0] == bom)
-        ++linePos;
-
-    if (line.length() < fileIdentifierLength + linePos)
+    if (!line.startsWith(fileIdentifier, fileIdentifierLength))
         return false;
 
-    for (unsigned i = 0; i < fileIdentifierLength; ++i, ++linePos) {
-        if (line[linePos] != fileIdentifier[i])
-            return false;
-    }
-
-    if (linePos < line.length() && line[linePos] != ' ' && line[linePos] != '\t')
+    if (line.length() > fileIdentifierLength && !isASpace(line[fileIdentifierLength]))
         return false;
     return true;
 }
@@ -287,10 +294,10 @@ void WebVTTParser::collectMetadataHeader(const String& line)
     // Step 12.4 If line contains the character ":" (A U+003A COLON), then set metadata's
     // name to the substring of line before the first ":" character and
     // metadata's value to the substring after this character.
-    if (!line.contains(":"))
+    size_t colonPosition = line.find(':');
+    if (colonPosition == notFound)
         return;
 
-    unsigned colonPosition = line.find(":");
     String headerName = line.substring(0, colonPosition);
 
     // Step 12.5 If metadata's name equals "Region":
@@ -317,16 +324,15 @@ WebVTTParser::ParseState WebVTTParser::collectTimingsAndSettings(const String& l
     // Collect WebVTT cue timings and settings. (5.3 WebVTT cue timings and settings parsing.)
     // Steps 1 - 3 - Let input be the string being parsed and position be a pointer into input
     unsigned position = 0;
-    skipWhiteSpace(line, &position);
+    skipWhiteSpace(line, position);
 
     // 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.
-    m_currentStartTime = collectTimeStamp(line, &position);
-    if (m_currentStartTime == malformedTime)
+    if (!collectTimeStamp(line, position, m_currentStartTime))
         return BadCue;
     if (position >= line.length())
         return BadCue;
     
-    skipWhiteSpace(line, &position);
+    skipWhiteSpace(line, position);
 
     // Steps 6 - 9 - If the next three characters are not "-->", abort and return failure.
     if (line.find("-->", position) == notFound)
@@ -335,13 +341,12 @@ WebVTTParser::ParseState WebVTTParser::collectTimingsAndSettings(const String& l
     if (position >= line.length())
         return BadCue;
     
-    skipWhiteSpace(line, &position);
+    skipWhiteSpace(line, position);
 
     // 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.
-    m_currentEndTime = collectTimeStamp(line, &position);
-    if (m_currentEndTime == malformedTime)
+    if (!collectTimeStamp(line, position, m_currentEndTime))
         return BadCue;
-    skipWhiteSpace(line, &position);
+    skipWhiteSpace(line, position);
 
     // Step 12 - Parse the WebVTT settings for the cue (conducted in TextTrackCue).
     m_currentSettings = line.substring(position, line.length()-1);
@@ -357,7 +362,7 @@ WebVTTParser::ParseState WebVTTParser::collectCueText(const String& line)
     if (!m_currentContent.isEmpty())
         m_currentContent.append("\n");
     m_currentContent.append(line);
-                
+
     return CueText;
 }
 
@@ -368,34 +373,54 @@ WebVTTParser::ParseState WebVTTParser::ignoreBadCue(const String& line)
     return Id;
 }
 
-PassRefPtr<DocumentFragment> WebVTTParser::createDocumentFragmentFromCueText(const String& text)
+// A helper class for the construction of a "cue fragment" from the cue text.
+class WebVTTTreeBuilder {
+public:
+    WebVTTTreeBuilder(Document& document)
+        : m_document(document) { }
+
+    PassRefPtr<DocumentFragment> buildFromString(const String& cueText);
+
+private:
+    void constructTreeFromToken(Document&);
+
+    WebVTTToken m_token;
+    RefPtr<ContainerNode> m_currentNode;
+    Vector<AtomicString> m_languageStack;
+    Document& m_document;
+};
+
+PassRefPtr<DocumentFragment> WebVTTTreeBuilder::buildFromString(const String& cueText)
 {
     // Cue text processing based on
     // 5.4 WebVTT cue text parsing rules, and
     // 5.5 WebVTT cue text DOM construction rules.
+    RefPtr<DocumentFragment> fragment = DocumentFragment::create(m_document);
 
-    ASSERT(m_scriptExecutionContext->isDocument());
-    Document* document = toDocument(m_scriptExecutionContext);
-    
-    RefPtr<DocumentFragment> fragment = DocumentFragment::create(*document);
-
-    if (text.isEmpty()) {
-        fragment->parserAppendChild(Text::create(*document, emptyString()));
+    if (cueText.isEmpty()) {
+        fragment->parserAppendChild(Text::create(m_document, emptyString()));
         return fragment.release();
     }
 
     m_currentNode = fragment;
-    m_tokenizer->reset();
+
+    std::unique_ptr<WebVTTTokenizer> tokenizer(std::make_unique<WebVTTTokenizer>());
     m_token.clear();
-    
     m_languageStack.clear();
-    SegmentedString content(text);
-    while (m_tokenizer->nextToken(content, m_token))
-        constructTreeFromToken(document);
+
+    SegmentedString content(cueText);
+    while (tokenizer->nextToken(content, m_token))
+        constructTreeFromToken(m_document);
     
     return fragment.release();
 }
 
+PassRefPtr<DocumentFragment> WebVTTParser::createDocumentFragmentFromCueText(Document& document, const String& cueText)
+{
+    WebVTTTreeBuilder treeBuilder(document);
+    return treeBuilder.buildFromString(cueText);
+}
+
 void WebVTTParser::createNewCue()
 {
     RefPtr<WebVTTCueData> cue = WebVTTCueData::create();
@@ -442,7 +467,7 @@ void WebVTTParser::createNewRegion(const String& headerValue)
 }
 #endif
 
-double WebVTTParser::collectTimeStamp(const String& line, unsigned* position)
+bool WebVTTParser::collectTimeStamp(const String& line, unsigned& position, double& timeStamp)
 {
     // Collect a WebVTT timestamp (5.3 WebVTT cue timings and settings parsing.)
     // Steps 1 - 4 - Initial checks, let most significant units be minutes.
@@ -454,24 +479,24 @@ double WebVTTParser::collectTimeStamp(const String& line, unsigned* position)
     int value1;
     unsigned value1Digits = collectDigitsToInt(line, position, value1);
     if (!value1Digits)
-        return malformedTime;
+        return false;
     if (value1Digits != 2 || value1 > 59)
         mode = hours;
 
     // Steps 8 - 11 - Collect the next sequence of 0-9 after ':' (must be 2 chars).
-    if (*position >= line.length() || line[(*position)++] != ':')
-        return malformedTime;
+    if (position >= line.length() || line[position++] != ':')
+        return false;
     int value2;
     if (collectDigitsToInt(line, position, value2) != 2)
-        return malformedTime;
+        return false;
 
     // Step 12 - Detect whether this timestamp includes hours.
     int value3;
-    if (mode == hours || (*position < line.length() && line[*position] == ':')) {
-        if (*position >= line.length() || line[(*position)++] != ':')
-            return malformedTime;
+    if (mode == hours || (position < line.length() && line[position] == ':')) {
+        if (position >= line.length() || line[position++] != ':')
+            return false;
         if (collectDigitsToInt(line, position, value3) != 2)
-            return malformedTime;
+            return false;
     } else {
         value3 = value2;
         value2 = value1;
@@ -479,16 +504,17 @@ double WebVTTParser::collectTimeStamp(const String& line, unsigned* position)
     }
 
     // Steps 13 - 17 - Collect next sequence of 0-9 after '.' (must be 3 chars).
-    if (*position >= line.length() || line[(*position)++] != '.')
-        return malformedTime;
+    if (position >= line.length() || line[position++] != '.')
+        return false;
     int value4;
     if (collectDigitsToInt(line, position, value4) != 3)
-        return malformedTime;
+        return false;
     if (value2 > 59 || value3 > 59)
-        return malformedTime;
+        return false;
 
     // Steps 18 - 19 - Calculate result.
-    return (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + (value4 * secondsPerMillisecond);
+    timeStamp = (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + (value4 * secondsPerMillisecond);
+    return true;
 }
 
 static WebVTTNodeType tokenToNodeType(WebVTTToken& token)
@@ -520,55 +546,75 @@ static WebVTTNodeType tokenToNodeType(WebVTTToken& token)
     return WebVTTNodeTypeNone;
 }
 
-void WebVTTParser::constructTreeFromToken(Document* document)
+void WebVTTTreeBuilder::constructTreeFromToken(Document& document)
 {
     // http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules
 
     switch (m_token.type()) {
     case WebVTTTokenTypes::Character: {
         String content = m_token.characters().toString();
-        RefPtr<Text> child = Text::create(*document, content);
+        RefPtr<Text> child = Text::create(document, content);
         m_currentNode->parserAppendChild(child);
         break;
     }
     case WebVTTTokenTypes::StartTag: {
-        RefPtr<WebVTTElement> child;
         WebVTTNodeType nodeType = tokenToNodeType(m_token);
-        if (nodeType != WebVTTNodeTypeNone)
-            child = WebVTTElement::create(nodeType, *document);
-        if (child) {
-            if (!m_token.classes().isEmpty())
-                child->setAttribute(classAttr, m_token.classes().toAtomicString());
-
-            if (child->webVTTNodeType() == WebVTTNodeTypeVoice)
-                child->setAttribute(WebVTTElement::voiceAttributeName(), m_token.annotation().toAtomicString());
-            else if (child->webVTTNodeType() == WebVTTNodeTypeLanguage) {
-                m_languageStack.append(m_token.annotation().toAtomicString());
-                child->setAttribute(WebVTTElement::langAttributeName(), m_languageStack.last());
-            }
-            if (!m_languageStack.isEmpty())
-                child->setLanguage(m_languageStack.last());
-            m_currentNode->parserAppendChild(child);
-            m_currentNode = child;
+        if (nodeType == WebVTTNodeTypeNone)
+            break;
+
+        WebVTTNodeType currentType = m_currentNode->isWebVTTElement() ? toWebVTTElement(m_currentNode.get())->webVTTNodeType() : WebVTTNodeTypeNone;
+        // <rt> is only allowed if the current node is <ruby>.
+        if (nodeType == WebVTTNodeTypeRubyText && currentType != WebVTTNodeTypeRuby)
+            break;
+
+        RefPtr<WebVTTElement> child = WebVTTElement::create(nodeType, document);
+        if (!m_token.classes().isEmpty())
+            child->setAttribute(classAttr, m_token.classes().toAtomicString());
+
+        if (nodeType == WebVTTNodeTypeVoice)
+            child->setAttribute(WebVTTElement::voiceAttributeName(), m_token.annotation().toAtomicString());
+        else if (nodeType == WebVTTNodeTypeLanguage) {
+            m_languageStack.append(m_token.annotation().toAtomicString());
+            child->setAttribute(WebVTTElement::langAttributeName(), m_languageStack.last());
         }
+        if (!m_languageStack.isEmpty())
+            child->setLanguage(m_languageStack.last());
+        m_currentNode->parserAppendChild(child);
+        m_currentNode = child;
         break;
     }
     case WebVTTTokenTypes::EndTag: {
         WebVTTNodeType nodeType = tokenToNodeType(m_token);
-        if (nodeType != WebVTTNodeTypeNone) {
-            if (nodeType == WebVTTNodeTypeLanguage && m_currentNode->isWebVTTElement() && toWebVTTElement(m_currentNode.get())->webVTTNodeType() == WebVTTNodeTypeLanguage)
-                m_languageStack.removeLast();
-            if (m_currentNode->parentNode())
-                m_currentNode = m_currentNode->parentNode();
+        if (nodeType == WebVTTNodeTypeNone)
+            break;
+        
+        // The only non-VTTElement would be the DocumentFragment root. (Text
+        // nodes and PIs will never appear as m_currentNode.)
+        if (!m_currentNode->isWebVTTElement())
+            break;
+
+        WebVTTNodeType currentType = toWebVTTElement(m_currentNode.get())->webVTTNodeType();
+        bool matchesCurrent = nodeType == currentType;
+        if (!matchesCurrent) {
+            // </ruby> auto-closes <rt>
+            if (currentType == WebVTTNodeTypeRubyText && nodeType == WebVTTNodeTypeRuby) {
+                if (m_currentNode->parentNode())
+                    m_currentNode = m_currentNode->parentNode();
+            } else
+                break;
         }
+        if (nodeType == WebVTTNodeTypeLanguage)
+            m_languageStack.removeLast();
+        if (m_currentNode->parentNode())
+            m_currentNode = m_currentNode->parentNode();
         break;
     }
     case WebVTTTokenTypes::TimestampTag: {
         unsigned position = 0;
         String charactersString = m_token.characters().toString();
-        double time = collectTimeStamp(charactersString, &position);
-        if (time != malformedTime)
-            m_currentNode->parserAppendChild(ProcessingInstruction::create(*document, "timestamp", charactersString));
+        double parsedTimeStamp;
+        if (WebVTTParser::collectTimeStamp(charactersString, position, parsedTimeStamp))
+            m_currentNode->parserAppendChild(ProcessingInstruction::create(document, "timestamp", charactersString));
         break;
     }
     default:
@@ -577,32 +623,10 @@ void WebVTTParser::constructTreeFromToken(Document* document)
     m_token.clear();
 }
 
-void WebVTTParser::skipWhiteSpace(const String& line, unsigned* position)
+void WebVTTParser::skipWhiteSpace(const String& line, unsigned& position)
 {
-    while (*position < line.length() && isASpace(line[*position]))
-        (*position)++;
-}
-
-String WebVTTParser::collectNextLine(const char* data, unsigned length, unsigned* position)
-{
-    unsigned currentPosition = *position;
-    while (currentPosition < length && data[currentPosition] != '\r' && data[currentPosition] != '\n')
-        currentPosition++;
-    if (currentPosition >= length)
-        return String();
-    String line = String::fromUTF8(data + *position , currentPosition - *position);
-    if (data[currentPosition] == '\r')
-        currentPosition++;
-    if (currentPosition < length && data[currentPosition] == '\n')
-        currentPosition++;
-    *position = currentPosition;
-    if (m_buffer.isEmpty())
-        return line;
-
-    String lineWithBuffer = String::fromUTF8(m_buffer.data(), m_buffer.size());
-    lineWithBuffer.append(line);
-    m_buffer.clear();
-    return lineWithBuffer;
+    while (position < line.length() && isASpace(line[position]))
+        position++;
 }
 
 }
index d665738..5167b0f 100644 (file)
 
 #if ENABLE(VIDEO_TRACK)
 
+#include "BufferedLineReader.h"
 #include "DocumentFragment.h"
 #include "HTMLNames.h"
+#include "TextResourceDecoder.h"
 #include "TextTrackRegion.h"
 #include "WebVTTTokenizer.h"
 #include <memory>
@@ -128,18 +130,19 @@ public:
         // U+0020 SPACE characters or U+0009 CHARACTER TABULATION (tab) characters.
         return c == ' ' || c == '\t';
     }
-    static unsigned collectDigitsToInt(const String&, unsigned* position, int& number);
+    static unsigned collectDigitsToInt(const String&, unsigned& position, int& number);
     static String collectWord(const String&, unsigned*);
-    static double collectTimeStamp(const String&, unsigned*);
+    static bool collectTimeStamp(const String&, unsigned&, double&);
 
 #if ENABLE(WEBVTT_REGIONS)
     // Useful functions for parsing percentage settings.
-    static float parseFloatPercentageValue(const String&, bool&);
-    static FloatPoint parseFloatPercentageValuePair(const String&, char, bool&);
+    static bool parseFloatPercentageValue(const String&, float&);
+    static bool parseFloatPercentageValuePair(const String&, char, FloatPoint&);
 #endif
 
     // Input data to the parser to parse.
     void parseBytes(const char* data, unsigned length);
+    void flush();
     void fileFinished();
 
     // Transfers ownership of last parsed cues to caller.
@@ -148,13 +151,16 @@ public:
     void getNewRegions(Vector<RefPtr<TextTrackRegion>>&);
 #endif
 
-    PassRefPtr<DocumentFragment> createDocumentFragmentFromCueText(const String&);
+    // Create the DocumentFragment representation of the WebVTT cue text.
+    static PassRefPtr<DocumentFragment> createDocumentFragmentFromCueText(Document&, const String&);
 
 protected:
     ScriptExecutionContext* m_scriptExecutionContext;
     ParseState m_state;
 
 private:
+    void parse();
+    void flushPendingCue();
     bool hasRequiredFileIdentifier(const String&);
     ParseState collectCueId(const String&);
     ParseState collectTimingsAndSettings(const String&);
@@ -169,27 +175,18 @@ private:
     void createNewRegion(const String& headerValue);
 #endif
 
-    static void skipWhiteSpace(const String&, unsigned*);
+    static void skipWhiteSpace(const String&, unsigned&);
 
-    String collectNextLine(const char* data, unsigned length, unsigned*);
-
-    void constructTreeFromToken(Document*);
-
-    Vector<char> m_buffer;
+    BufferedLineReader m_lineReader;
+    RefPtr<TextResourceDecoder> m_decoder;
     String m_currentId;
     double m_currentStartTime;
     double m_currentEndTime;
     StringBuilder m_currentContent;
     String m_currentSettings;
     
-    WebVTTToken m_token;
-    std::unique_ptr<WebVTTTokenizer> m_tokenizer;
-
-    RefPtr<ContainerNode> m_currentNode;
-
     WebVTTParserClient* m_client;
 
-    Vector<AtomicString> m_languageStack;
     Vector<RefPtr<WebVTTCueData>> m_cuelist;
 
 #if ENABLE(WEBVTT_REGIONS)
index 86fcaaa..8aedcf8 100644 (file)
@@ -141,6 +141,9 @@ void TextTrackLoader::notifyFinished(CachedResource* resource)
             m_state = resource->errorOccurred() ? Failed : Finished;
     }
 
+    if (m_state == Finished && m_cueParser)
+        m_cueParser->flush();
+
     if (!m_cueLoadTimer.isActive())
         m_cueLoadTimer.startOneShot(0);