Add handling for a case of OOME in CSSTokenizer and CSSParser.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jun 2020 21:17:01 +0000 (21:17 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jun 2020 21:17:01 +0000 (21:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=213702
<rdar://problem/64808889>

Reviewed by Darin Adler.

Source/WebCore:

We add a bool* constructionSuccess feedback argument to the private CSSTokenizer
constructor.  If construction fails and constructionSuccess is provided, the
constructor will set *constructionSuccess to false.  If construction fails and
constructionSuccess is not provided, the constructor will crash with a failed
RELEASE_ASSERT.  In other words, the client may opt in to handle the failure to
construct if it doesn't want the default behavior of crashing on failure.

We also provide 2 convenience factory methods for CSSTokenizer which will return
a null std::unique_ptr<CSSTokenizer> if construction fails.  This is currently
only used by CSSParserImpl, and ensures that its m_tokenizer is null if we fail to
construct.  This ensures that there isn't a pointer to a partially constructed
tokenizer that some code may unknowingly use.

The reason we don't force all clients of CSSTokenizer to use the factory methods
instead is because there are clients that currently use on-stack instantiations
of CSSTokenizer to do their work.  We don't want to force them to switch to using
a malloc instance.  Currently, the constructors used by those clients do not
provide a constructionSuccess argument to the underlying private constructor.
Hence, for them, the CSSTokenizer constructor will crash if construction fails,
which is how things work in pre-existing code.  The only difference is that
the crash is deferred till the client attempts to use the tokenizer instead of at
construction time.

As of this patch, only CSSParser::parseSupportsCondition() makes use of the new
feedback mechanism, and handles OOME during CSSTokenizer construction by
interpreting it as CSS not supporting the passed in condition string.

Test: css3/out-of-memory-in-css-tokenizer.html

* css/parser/CSSParser.cpp:
(WebCore::CSSParser::parseSupportsCondition):
* css/parser/CSSParserImpl.cpp:
(WebCore::CSSParserImpl::CSSParserImpl):
(WebCore::CSSParserImpl::failed const):
* css/parser/CSSParserImpl.h:
* css/parser/CSSTokenizer.cpp:
(WebCore::CSSTokenizer::CSSTokenizer):
* css/parser/CSSTokenizer.h:
(WebCore::CSSTokenizer::failed const):

Source/WTF:

1. Added FailureAction so that we can parameterize how we want to handle failures.
   In this patch, we're only using this for allocation failures, but we could
   technically apply this to other types of failures as well.

2. Apply FailureAction to many methods in Vector (and its super classes) so that
   we can start de-duplicating code.  Previously, we were always duplicating code
   just to have a "try" version of the same method that reports the failure to
   allocate instead of crashing.  We can now parameterize all these methods on a
   FailureAction template parameter instead, and avoid the code duplication.
   This patch also reverses some of the existing code duplication.

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/FailureAction.h: Added.
* wtf/Vector.h:
(WTF::VectorBufferBase::allocateBuffer):
(WTF::VectorBufferBase::tryAllocateBuffer):
(WTF::VectorBuffer::allocateBuffer):
(WTF::VectorBuffer::tryAllocateBuffer):
(WTF::Vector::reserveCapacity):
(WTF::Vector::tryReserveCapacity):
(WTF::Vector::reserveInitialCapacity):
(WTF::Vector::tryReserveInitialCapacity):
(WTF::Vector::append):
(WTF::Vector::tryAppend):
(WTF::Vector::constructAndAppend):
(WTF::Vector::tryConstructAndAppend):
(WTF::Vector::expandCapacity):
(WTF::Vector::resize):
(WTF::Vector::grow):
(WTF::Vector::reserveCapacity):
(WTF::Vector::reserveInitialCapacity):
(WTF::Vector::append):
(WTF::Vector::constructAndAppend):
(WTF::Vector::appendSlowCase):
(WTF::Vector::constructAndAppendSlowCase):
(WTF::Vector::appendVector):
(WTF::Vector::insert):
(WTF::Vector::tryExpandCapacity): Deleted.
(WTF::Vector::tryReserveCapacity): Deleted.
(WTF::Vector::tryAppend): Deleted.
(WTF::Vector::tryConstructAndAppend): Deleted.
(WTF::Vector::tryConstructAndAppendSlowCase): Deleted.

LayoutTests:

* css3/out-of-memory-in-css-tokenizer-expected.txt: Added.
* css3/out-of-memory-in-css-tokenizer.html: Added.

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

14 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/out-of-memory-in-css-tokenizer-expected.txt [new file with mode: 0644]
LayoutTests/css3/out-of-memory-in-css-tokenizer.html [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/FailureAction.h [new file with mode: 0644]
Source/WTF/wtf/Vector.h
Source/WebCore/ChangeLog
Source/WebCore/css/parser/CSSParser.cpp
Source/WebCore/css/parser/CSSParserImpl.cpp
Source/WebCore/css/parser/CSSParserImpl.h
Source/WebCore/css/parser/CSSTokenizer.cpp
Source/WebCore/css/parser/CSSTokenizer.h

index 6c241c6..a966388 100644 (file)
@@ -1,3 +1,14 @@
+2020-06-30  Mark Lam  <mark.lam@apple.com>
+
+        Add handling for a case of OOME in CSSTokenizer and CSSParser.
+        https://bugs.webkit.org/show_bug.cgi?id=213702
+        <rdar://problem/64808889>
+
+        Reviewed by Darin Adler.
+
+        * css3/out-of-memory-in-css-tokenizer-expected.txt: Added.
+        * css3/out-of-memory-in-css-tokenizer.html: Added.
+
 2020-06-30  Karl Rackler  <rackler@apple.com>
 
         Remove expectation for fast/attachment/attachment-folder-icon.html as it is passing. 
diff --git a/LayoutTests/css3/out-of-memory-in-css-tokenizer-expected.txt b/LayoutTests/css3/out-of-memory-in-css-tokenizer-expected.txt
new file mode 100644 (file)
index 0000000..fbd0592
--- /dev/null
@@ -0,0 +1,5 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
diff --git a/LayoutTests/css3/out-of-memory-in-css-tokenizer.html b/LayoutTests/css3/out-of-memory-in-css-tokenizer.html
new file mode 100644 (file)
index 0000000..b3e7c0c
--- /dev/null
@@ -0,0 +1,3 @@
+<script>
+CSS.supports('a'.repeat(2**29 + 1));
+</script>
index 34377cc..820ada2 100644 (file)
@@ -1,3 +1,55 @@
+2020-06-30  Mark Lam  <mark.lam@apple.com>
+
+        Add handling for a case of OOME in CSSTokenizer and CSSParser.
+        https://bugs.webkit.org/show_bug.cgi?id=213702
+        <rdar://problem/64808889>
+
+        Reviewed by Darin Adler.
+
+        1. Added FailureAction so that we can parameterize how we want to handle failures.
+           In this patch, we're only using this for allocation failures, but we could
+           technically apply this to other types of failures as well.
+
+        2. Apply FailureAction to many methods in Vector (and its super classes) so that
+           we can start de-duplicating code.  Previously, we were always duplicating code
+           just to have a "try" version of the same method that reports the failure to
+           allocate instead of crashing.  We can now parameterize all these methods on a
+           FailureAction template parameter instead, and avoid the code duplication.
+           This patch also reverses some of the existing code duplication.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/FailureAction.h: Added.
+        * wtf/Vector.h:
+        (WTF::VectorBufferBase::allocateBuffer):
+        (WTF::VectorBufferBase::tryAllocateBuffer):
+        (WTF::VectorBuffer::allocateBuffer):
+        (WTF::VectorBuffer::tryAllocateBuffer):
+        (WTF::Vector::reserveCapacity):
+        (WTF::Vector::tryReserveCapacity):
+        (WTF::Vector::reserveInitialCapacity):
+        (WTF::Vector::tryReserveInitialCapacity):
+        (WTF::Vector::append):
+        (WTF::Vector::tryAppend):
+        (WTF::Vector::constructAndAppend):
+        (WTF::Vector::tryConstructAndAppend):
+        (WTF::Vector::expandCapacity):
+        (WTF::Vector::resize):
+        (WTF::Vector::grow):
+        (WTF::Vector::reserveCapacity):
+        (WTF::Vector::reserveInitialCapacity):
+        (WTF::Vector::append):
+        (WTF::Vector::constructAndAppend):
+        (WTF::Vector::appendSlowCase):
+        (WTF::Vector::constructAndAppendSlowCase):
+        (WTF::Vector::appendVector):
+        (WTF::Vector::insert):
+        (WTF::Vector::tryExpandCapacity): Deleted.
+        (WTF::Vector::tryReserveCapacity): Deleted.
+        (WTF::Vector::tryAppend): Deleted.
+        (WTF::Vector::tryConstructAndAppend): Deleted.
+        (WTF::Vector::tryConstructAndAppendSlowCase): Deleted.
+
 2020-06-30  Peng Liu  <peng.liu6@apple.com>
 
         Enable the support of FULLSCREEN_API in WebKitTestRunner
index 4e70c5e..fc9ce1c 100644 (file)
                FE86A8741E59440200111BBF /* ForbidHeapAllocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ForbidHeapAllocation.h; sourceTree = "<group>"; };
                FE8925AF1D00DAEC0046907E /* Indenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Indenter.h; sourceTree = "<group>"; };
                FE97F6A8245CE5DD00C63FC6 /* StdIntExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StdIntExtras.h; sourceTree = "<group>"; };
+               FE996B5224A845AF001D2E63 /* FailureAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FailureAction.h; sourceTree = "<group>"; };
                FEB6B035201BE0B600B958C1 /* PointerPreparations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointerPreparations.h; sourceTree = "<group>"; };
                FEDACD3B1630F83F00C69634 /* StackStats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StackStats.cpp; sourceTree = "<group>"; };
                FEDACD3C1630F83F00C69634 /* StackStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackStats.h; sourceTree = "<group>"; };
                                1AEA88E11D6BBCF400E5AD64 /* EnumTraits.h */,
                                AD7C434A1DD2A4A70026888B /* Expected.h */,
                                A8A4729F151A825A004123FF /* ExportMacros.h */,
+                               FE996B5224A845AF001D2E63 /* FailureAction.h */,
                                0F7C5FB51D885CF20044F5E2 /* FastBitVector.cpp */,
                                0FD81AC4154FB22E00983E72 /* FastBitVector.h */,
                                A8A472A1151A825A004123FF /* FastMalloc.cpp */,
index 1a38088..e7c4fc1 100644 (file)
@@ -62,6 +62,7 @@ set(WTF_PUBLIC_HEADERS
     EnumTraits.h
     Expected.h
     ExportMacros.h
+    FailureAction.h
     FastBitVector.h
     FastMalloc.h
     FastTLS.h
diff --git a/Source/WTF/wtf/FailureAction.h b/Source/WTF/wtf/FailureAction.h
new file mode 100644 (file)
index 0000000..5d3c66b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#pragma once
+
+namespace WTF {
+
+enum class FailureAction {
+    Crash,
+    Report
+};
+
+} // namespace WTF
index a56e4a5..eaf1fb4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2005-2019 Apple Inc. All rights reserved.
+ *  Copyright (C) 2005-2020 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -26,6 +26,7 @@
 #include <type_traits>
 #include <utility>
 #include <wtf/CheckedArithmetic.h>
+#include <wtf/FailureAction.h>
 #include <wtf/FastMalloc.h>
 #include <wtf/Forward.h>
 #include <wtf/MallocPtr.h>
@@ -282,31 +283,35 @@ template<typename T, typename Malloc>
 class VectorBufferBase {
     WTF_MAKE_NONCOPYABLE(VectorBufferBase);
 public:
-    void allocateBuffer(size_t newCapacity)
+    template<FailureAction action>
+    bool allocateBuffer(size_t newCapacity)
     {
+        static_assert(action == FailureAction::Crash || action == FailureAction::Report);
         ASSERT(newCapacity);
-        if (newCapacity > std::numeric_limits<unsigned>::max() / sizeof(T))
-            CRASH();
-        size_t sizeToAllocate = newCapacity * sizeof(T);
-        m_capacity = sizeToAllocate / sizeof(T);
-        m_buffer = static_cast<T*>(Malloc::malloc(sizeToAllocate));
-    }
-
-    bool tryAllocateBuffer(size_t newCapacity)
-    {
-        ASSERT(newCapacity);
-        if (newCapacity > std::numeric_limits<unsigned>::max() / sizeof(T))
-            return false;
+        if (newCapacity > std::numeric_limits<unsigned>::max() / sizeof(T)) {
+            if constexpr (action == FailureAction::Crash)
+                CRASH();
+            else
+                return false;
+        }
 
         size_t sizeToAllocate = newCapacity * sizeof(T);
-        T* newBuffer = static_cast<T*>(Malloc::tryMalloc(sizeToAllocate));
-        if (!newBuffer)
-            return false;
+        T* newBuffer = nullptr;
+        if constexpr (action == FailureAction::Crash)
+            newBuffer = static_cast<T*>(Malloc::malloc(sizeToAllocate));
+        else {
+            newBuffer = static_cast<T*>(Malloc::tryMalloc(sizeToAllocate));
+            if (UNLIKELY(!newBuffer))
+                return false;
+        }
         m_capacity = sizeToAllocate / sizeof(T);
         m_buffer = newBuffer;
         return true;
     }
 
+    ALWAYS_INLINE void allocateBuffer(size_t newCapacity) { allocateBuffer<FailureAction::Crash>(newCapacity); }
+    ALWAYS_INLINE bool tryAllocateBuffer(size_t newCapacity) { return allocateBuffer<FailureAction::Report>(newCapacity); }
+
     bool shouldReallocateBuffer(size_t newCapacity) const
     {
         return VectorTraits<T>::canMoveWithMemcpy && m_capacity && newCapacity;
@@ -457,26 +462,20 @@ public:
         deallocateBuffer(buffer());
     }
 
-    void allocateBuffer(size_t newCapacity)
+    template<FailureAction action>
+    bool allocateBuffer(size_t newCapacity)
     {
         // FIXME: This should ASSERT(!m_buffer) to catch misuse/leaks.
         if (newCapacity > inlineCapacity)
-            Base::allocateBuffer(newCapacity);
-        else {
-            m_buffer = inlineBuffer();
-            m_capacity = inlineCapacity;
-        }
-    }
-
-    bool tryAllocateBuffer(size_t newCapacity)
-    {
-        if (newCapacity > inlineCapacity)
-            return Base::tryAllocateBuffer(newCapacity);
+            return Base::template allocateBuffer<action>(newCapacity);
         m_buffer = inlineBuffer();
         m_capacity = inlineCapacity;
         return true;
     }
 
+    ALWAYS_INLINE void allocateBuffer(size_t newCapacity) { allocateBuffer<FailureAction::Crash>(newCapacity); }
+    ALWAYS_INLINE bool tryAllocateBuffer(size_t newCapacity) { return allocateBuffer<FailureAction::Report>(newCapacity); }
+
     void deallocateBuffer(T* bufferToDeallocate)
     {
         if (bufferToDeallocate == inlineBuffer())
@@ -761,9 +760,10 @@ public:
     void grow(size_t size);
     void resize(size_t size);
     void resizeToFit(size_t size);
-    void reserveCapacity(size_t newCapacity);
-    bool tryReserveCapacity(size_t newCapacity);
-    void reserveInitialCapacity(size_t initialCapacity);
+    ALWAYS_INLINE void reserveCapacity(size_t newCapacity) { reserveCapacity<FailureAction::Crash>(newCapacity); }
+    ALWAYS_INLINE bool tryReserveCapacity(size_t newCapacity) { return reserveCapacity<FailureAction::Report>(newCapacity); }
+    ALWAYS_INLINE void reserveInitialCapacity(size_t initialCapacity) { reserveInitialCapacity<FailureAction::Crash>(initialCapacity); }
+    ALWAYS_INLINE bool tryReserveInitialCapacity(size_t initialCapacity) { return reserveInitialCapacity<FailureAction::Report>(initialCapacity); }
     void shrinkCapacity(size_t newCapacity);
     void shrinkToFit() { shrinkCapacity(size()); }
 
@@ -772,18 +772,19 @@ public:
     template<typename U = T> Vector<U> isolatedCopy() const;
 
     ALWAYS_INLINE void append(ValueType&& value) { append<ValueType>(std::forward<ValueType>(value)); }
-    template<typename U> void append(U&&);
-    template<typename... Args> void constructAndAppend(Args&&...);
-    template<typename... Args> bool tryConstructAndAppend(Args&&...);
+    template<typename U> ALWAYS_INLINE void append(U&& u) { append<FailureAction::Crash, U>(std::forward<U>(u)); }
+    template<typename U> ALWAYS_INLINE bool tryAppend(U&& u) { return append<FailureAction::Report, U>(std::forward<U>(u)); }
+    template<typename... Args> ALWAYS_INLINE void constructAndAppend(Args&&... args) { constructAndAppend<FailureAction::Crash>(std::forward<Args>(args)...); }
+    template<typename... Args> ALWAYS_INLINE bool tryConstructAndAppend(Args&&... args) { return constructAndAppend<FailureAction::Report>(std::forward<Args>(args)...); }
 
     void uncheckedAppend(ValueType&& value) { uncheckedAppend<ValueType>(std::forward<ValueType>(value)); }
     template<typename U> void uncheckedAppend(U&&);
     template<typename... Args> void uncheckedConstructAndAppend(Args&&...);
 
-    template<typename U> void append(const U*, size_t);
+    template<typename U> ALWAYS_INLINE void append(const U* u, size_t size) { append<FailureAction::Crash>(u, size); }
+    template<typename U> ALWAYS_INLINE bool tryAppend(const U* u, size_t size) { return append<FailureAction::Report>(u, size); }
     template<typename U, size_t otherCapacity> void appendVector(const Vector<U, otherCapacity>&);
     template<typename U, size_t otherCapacity> void appendVector(Vector<U, otherCapacity>&&);
-    template<typename U> bool tryAppend(const U*, size_t);
 
     template<typename U> void insert(size_t position, const U*, size_t);
     template<typename U> void insert(size_t position, U&&);
@@ -837,14 +838,18 @@ public:
     bool isHashTableDeletedValue() const { return m_size == std::numeric_limits<decltype(m_size)>::max(); }
 
 private:
-    void expandCapacity(size_t newMinCapacity);
-    T* expandCapacity(size_t newMinCapacity, T*);
-    bool tryExpandCapacity(size_t newMinCapacity);
-    const T* tryExpandCapacity(size_t newMinCapacity, const T*);
-    template<typename U> U* expandCapacity(size_t newMinCapacity, U*); 
-    template<typename U> void appendSlowCase(U&&);
-    template<typename... Args> void constructAndAppendSlowCase(Args&&...);
-    template<typename... Args> bool tryConstructAndAppendSlowCase(Args&&...);
+    template<FailureAction> bool reserveCapacity(size_t newCapacity);
+    template<FailureAction> bool reserveInitialCapacity(size_t initialCapacity);
+
+    template<FailureAction> bool expandCapacity(size_t newMinCapacity);
+    template<FailureAction> T* expandCapacity(size_t newMinCapacity, T*);
+    template<FailureAction, typename U> U* expandCapacity(size_t newMinCapacity, U*);
+    template<FailureAction, typename U> bool appendSlowCase(U&&);
+    template<FailureAction, typename... Args> bool constructAndAppend(Args&&...);
+    template<FailureAction, typename... Args> bool constructAndAppendSlowCase(Args&&...);
+
+    template<FailureAction, typename U> bool append(U&&);
+    template<FailureAction, typename U> bool append(const U*, size_t);
 
     template<size_t position, typename U, typename... Items>
     void uncheckedInitialize(U&& item, Items&&... items)
@@ -1043,48 +1048,44 @@ void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::appendRang
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::expandCapacity(size_t newMinCapacity)
+template<FailureAction action>
+bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::expandCapacity(size_t newMinCapacity)
 {
-    reserveCapacity(std::max(newMinCapacity, std::max(static_cast<size_t>(minCapacity), capacity() + capacity() / 4 + 1)));
+    return reserveCapacity<action>(std::max(newMinCapacity, std::max(static_cast<size_t>(minCapacity), capacity() + capacity() / 4 + 1)));
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
+template<FailureAction action>
 NEVER_INLINE T* Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::expandCapacity(size_t newMinCapacity, T* ptr)
 {
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
     if (ptr < begin() || ptr >= end()) {
-        expandCapacity(newMinCapacity);
+        bool success = expandCapacity<action>(newMinCapacity);
+        if constexpr (action == FailureAction::Report) {
+            if (UNLIKELY(!success))
+                return nullptr;
+        }
         return ptr;
     }
     size_t index = ptr - begin();
-    expandCapacity(newMinCapacity);
-    return begin() + index;
-}
-
-template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryExpandCapacity(size_t newMinCapacity)
-{
-    return tryReserveCapacity(std::max(newMinCapacity, std::max(static_cast<size_t>(minCapacity), capacity() + capacity() / 4 + 1)));
-}
-
-template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-const T* Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryExpandCapacity(size_t newMinCapacity, const T* ptr)
-{
-    if (ptr < begin() || ptr >= end()) {
-        if (!tryExpandCapacity(newMinCapacity))
-            return 0;
-        return ptr;
+    bool success = expandCapacity<action>(newMinCapacity);
+    if constexpr (action == FailureAction::Report) {
+        if (UNLIKELY(!success))
+            return nullptr;
     }
-    size_t index = ptr - begin();
-    if (!tryExpandCapacity(newMinCapacity))
-        return 0;
     return begin() + index;
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename U>
+template<FailureAction action, typename U>
 inline U* Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::expandCapacity(size_t newMinCapacity, U* ptr)
 {
-    expandCapacity(newMinCapacity);
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
+    bool success = expandCapacity<action>(newMinCapacity);
+    if constexpr (action == FailureAction::Report) {
+        if (UNLIKELY(!success))
+            return nullptr;
+    }
     return ptr;
 }
 
@@ -1096,7 +1097,7 @@ inline void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::res
         asanBufferSizeWillChangeTo(size);
     } else {
         if (size > capacity())
-            expandCapacity(size);
+            expandCapacity<FailureAction::Crash>(size);
         asanBufferSizeWillChangeTo(size);
         if (begin())
             TypeOperations::initializeIfNonPOD(end(), begin() + size);
@@ -1126,7 +1127,7 @@ void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::grow(size_
 {
     ASSERT(size >= m_size);
     if (size > capacity())
-        expandCapacity(size);
+        expandCapacity<FailureAction::Crash>(size);
     asanBufferSizeWillChangeTo(size);
     if (begin())
         TypeOperations::initializeIfNonPOD(end(), begin() + size);
@@ -1178,27 +1179,10 @@ inline void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::asa
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::reserveCapacity(size_t newCapacity)
-{
-    if (newCapacity <= capacity())
-        return;
-    T* oldBuffer = begin();
-    T* oldEnd = end();
-
-    asanSetBufferSizeToFullCapacity();
-
-    Base::allocateBuffer(newCapacity);
-    ASSERT(begin());
-
-    asanSetInitialBufferSizeTo(size());
-
-    TypeOperations::move(oldBuffer, oldEnd, begin());
-    Base::deallocateBuffer(oldBuffer);
-}
-
-template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryReserveCapacity(size_t newCapacity)
+template<FailureAction action>
+bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::reserveCapacity(size_t newCapacity)
 {
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
     if (newCapacity <= capacity())
         return true;
     T* oldBuffer = begin();
@@ -1206,9 +1190,12 @@ bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryReserve
 
     asanSetBufferSizeToFullCapacity();
 
-    if (!Base::tryAllocateBuffer(newCapacity)) {
-        asanSetInitialBufferSizeTo(size());
-        return false;
+    bool success = Base::template allocateBuffer<action>(newCapacity);
+    if constexpr (action == FailureAction::Report) {
+        if (UNLIKELY(!success)) {
+            asanSetInitialBufferSizeTo(size());
+            return false;
+        }
     }
     ASSERT(begin());
 
@@ -1220,12 +1207,15 @@ bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryReserve
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-inline void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::reserveInitialCapacity(size_t initialCapacity)
+template<FailureAction action>
+inline bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::reserveInitialCapacity(size_t initialCapacity)
 {
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
     ASSERT(!m_size);
     ASSERT(capacity() == inlineCapacity);
-    if (initialCapacity > inlineCapacity)
-        Base::allocateBuffer(initialCapacity);
+    if (initialCapacity <= inlineCapacity)
+        return true;
+    return Base::template allocateBuffer<action>(initialCapacity);
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
@@ -1260,35 +1250,25 @@ void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::shrinkCapa
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename U>
-ALWAYS_INLINE void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::append(const U* data, size_t dataSize)
+template<FailureAction action, typename U>
+ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::append(const U* data, size_t dataSize)
 {
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
     size_t newSize = m_size + dataSize;
     if (newSize > capacity()) {
-        data = expandCapacity(newSize, data);
+        data = expandCapacity<action>(newSize, data);
+        if constexpr (action == FailureAction::Report) {
+            if (UNLIKELY(!data))
+                return false;
+        }
         ASSERT(begin());
     }
-    if (newSize < m_size)
-        CRASH();
-    asanBufferSizeWillChangeTo(newSize);
-    T* dest = end();
-    VectorCopier<std::is_trivial<T>::value, U>::uninitializedCopy(data, std::addressof(data[dataSize]), dest);
-    m_size = newSize;
-}
-
-template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename U>
-ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryAppend(const U* data, size_t dataSize)
-{
-    size_t newSize = m_size + dataSize;
-    if (newSize > capacity()) {
-        data = tryExpandCapacity(newSize, data);
-        if (!data)
+    if (newSize < m_size) {
+        if constexpr (action == FailureAction::Crash)
+            CRASH();
+        else
             return false;
-        ASSERT(begin());
     }
-    if (newSize < m_size)
-        return false;
     asanBufferSizeWillChangeTo(newSize);
     T* dest = end();
     VectorCopier<std::is_trivial<T>::value, U>::uninitializedCopy(data, std::addressof(data[dataSize]), dest);
@@ -1297,36 +1277,22 @@ ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Mallo
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename U>
-ALWAYS_INLINE void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::append(U&& value)
+template<FailureAction action, typename U>
+ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::append(U&& value)
 {
     if (size() != capacity()) {
         asanBufferSizeWillChangeTo(m_size + 1);
         new (NotNull, end()) T(std::forward<U>(value));
         ++m_size;
-        return;
-    }
-
-    appendSlowCase(std::forward<U>(value));
-}
-
-template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename... Args>
-ALWAYS_INLINE void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::constructAndAppend(Args&&... args)
-{
-    if (size() != capacity()) {
-        asanBufferSizeWillChangeTo(m_size + 1);
-        new (NotNull, end()) T(std::forward<Args>(args)...);
-        ++m_size;
-        return;
+        return true;
     }
 
-    constructAndAppendSlowCase(std::forward<Args>(args)...);
+    return appendSlowCase<action, U>(std::forward<U>(value));
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename... Args>
-ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryConstructAndAppend(Args&&... args)
+template<FailureAction action, typename... Args>
+ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::constructAndAppend(Args&&... args)
 {
     if (size() != capacity()) {
         asanBufferSizeWillChangeTo(m_size + 1);
@@ -1334,52 +1300,48 @@ ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Mallo
         ++m_size;
         return true;
     }
-    
-    return tryConstructAndAppendSlowCase(std::forward<Args>(args)...);
+
+    return constructAndAppendSlowCase<action>(std::forward<Args>(args)...);
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename U>
-void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::appendSlowCase(U&& value)
+template<FailureAction action, typename U>
+bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::appendSlowCase(U&& value)
 {
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
     ASSERT(size() == capacity());
 
     auto ptr = const_cast<typename std::remove_const<typename std::remove_reference<U>::type>::type*>(std::addressof(value));
-    ptr = expandCapacity(size() + 1, ptr);
+    ptr = expandCapacity<action>(size() + 1, ptr);
+    if constexpr (action == FailureAction::Report) {
+        if (UNLIKELY(!ptr))
+            return false;
+    }
     ASSERT(begin());
 
     asanBufferSizeWillChangeTo(m_size + 1);
     new (NotNull, end()) T(std::forward<U>(*ptr));
     ++m_size;
+    return true;
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename... Args>
-void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::constructAndAppendSlowCase(Args&&... args)
+template<FailureAction action, typename... Args>
+bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::constructAndAppendSlowCase(Args&&... args)
 {
+    static_assert(action == FailureAction::Crash || action == FailureAction::Report);
     ASSERT(size() == capacity());
 
-    expandCapacity(size() + 1);
+    bool success = expandCapacity<action>(size() + 1);
+    if constexpr (action == FailureAction::Report) {
+        if (UNLIKELY(!success))
+            return false;
+    }
     ASSERT(begin());
 
     asanBufferSizeWillChangeTo(m_size + 1);
     new (NotNull, end()) T(std::forward<Args>(args)...);
     ++m_size;
-}
-
-template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
-template<typename... Args>
-bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::tryConstructAndAppendSlowCase(Args&&... args)
-{
-    ASSERT(size() == capacity());
-    
-    if (UNLIKELY(!tryExpandCapacity(size() + 1)))
-        return false;
-    ASSERT(begin());
-    
-    asanBufferSizeWillChangeTo(m_size + 1);
-    new (NotNull, end()) T(std::forward<Args>(args)...);
-    ++m_size;
     return true;
 }
 
@@ -1423,7 +1385,7 @@ inline void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::app
 {
     size_t newSize = m_size + val.size();
     if (newSize > capacity())
-        expandCapacity(newSize);
+        expandCapacity<FailureAction::Crash>(newSize);
     for (auto& item : val)
         uncheckedAppend(WTFMove(item));
 }
@@ -1435,7 +1397,7 @@ void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::insert(siz
     ASSERT_WITH_SECURITY_IMPLICATION(position <= size());
     size_t newSize = m_size + dataSize;
     if (newSize > capacity()) {
-        data = expandCapacity(newSize, data);
+        data = expandCapacity<FailureAction::Crash>(newSize, data);
         ASSERT(begin());
     }
     if (newSize < m_size)
@@ -1455,7 +1417,7 @@ inline void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::ins
 
     auto ptr = const_cast<typename std::remove_const<typename std::remove_reference<U>::type>::type*>(std::addressof(value));
     if (size() == capacity()) {
-        ptr = expandCapacity(size() + 1, ptr);
+        ptr = expandCapacity<FailureAction::Crash>(size() + 1, ptr);
         ASSERT(begin());
     }
 
index a361eac..830ba19 100644 (file)
@@ -1,3 +1,51 @@
+2020-06-30  Mark Lam  <mark.lam@apple.com>
+
+        Add handling for a case of OOME in CSSTokenizer and CSSParser.
+        https://bugs.webkit.org/show_bug.cgi?id=213702
+        <rdar://problem/64808889>
+
+        Reviewed by Darin Adler.
+
+        We add a bool* constructionSuccess feedback argument to the private CSSTokenizer
+        constructor.  If construction fails and constructionSuccess is provided, the
+        constructor will set *constructionSuccess to false.  If construction fails and
+        constructionSuccess is not provided, the constructor will crash with a failed
+        RELEASE_ASSERT.  In other words, the client may opt in to handle the failure to
+        construct if it doesn't want the default behavior of crashing on failure.
+
+        We also provide 2 convenience factory methods for CSSTokenizer which will return
+        a null std::unique_ptr<CSSTokenizer> if construction fails.  This is currently
+        only used by CSSParserImpl, and ensures that its m_tokenizer is null if we fail to
+        construct.  This ensures that there isn't a pointer to a partially constructed
+        tokenizer that some code may unknowingly use.
+
+        The reason we don't force all clients of CSSTokenizer to use the factory methods
+        instead is because there are clients that currently use on-stack instantiations
+        of CSSTokenizer to do their work.  We don't want to force them to switch to using
+        a malloc instance.  Currently, the constructors used by those clients do not
+        provide a constructionSuccess argument to the underlying private constructor.
+        Hence, for them, the CSSTokenizer constructor will crash if construction fails,
+        which is how things work in pre-existing code.  The only difference is that
+        the crash is deferred till the client attempts to use the tokenizer instead of at
+        construction time.
+
+        As of this patch, only CSSParser::parseSupportsCondition() makes use of the new
+        feedback mechanism, and handles OOME during CSSTokenizer construction by
+        interpreting it as CSS not supporting the passed in condition string.
+
+        Test: css3/out-of-memory-in-css-tokenizer.html
+
+        * css/parser/CSSParser.cpp:
+        (WebCore::CSSParser::parseSupportsCondition):
+        * css/parser/CSSParserImpl.cpp:
+        (WebCore::CSSParserImpl::CSSParserImpl):
+        (WebCore::CSSParserImpl::failed const):
+        * css/parser/CSSParserImpl.h:
+        * css/parser/CSSTokenizer.cpp:
+        (WebCore::CSSTokenizer::CSSTokenizer):
+        * css/parser/CSSTokenizer.h:
+        (WebCore::CSSTokenizer::failed const):
+
 2020-06-30  Peng Liu  <peng.liu6@apple.com>
 
         Enable the support of FULLSCREEN_API in WebKitTestRunner
index 1b6179d..8d1e075 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2003 Lars Knoll (knoll@kde.org)
  * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
- * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
  * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
@@ -89,6 +89,8 @@ RefPtr<StyleRuleKeyframe> CSSParser::parseKeyframeRule(const String& string)
 bool CSSParser::parseSupportsCondition(const String& condition)
 {
     CSSParserImpl parser(m_context, condition);
+    if (!parser.tokenizer())
+        return false;
     return CSSSupportsParser::supportsCondition(parser.tokenizer()->tokenRange(), parser, CSSSupportsParser::ForWindowCSS) == CSSSupportsParser::Supported;
 }
 
index c7b2f68..60348bd 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright 2014 The Chromium Authors. All rights reserved.
-// Copyright (C) 2016 Apple Inc. All rights reserved.
+// Copyright (C) 2016-2020 Apple Inc. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
@@ -77,7 +77,9 @@ CSSParserImpl::CSSParserImpl(const CSSParserContext& context, const String& stri
     , m_styleSheet(styleSheet)
     , m_observerWrapper(wrapper)
 {
-    m_tokenizer = wrapper ? makeUnique<CSSTokenizer>(string, *wrapper) : makeUnique<CSSTokenizer>(string);
+    m_tokenizer = wrapper ? CSSTokenizer::tryCreate(string, *wrapper) : CSSTokenizer::tryCreate(string);
+    if (!m_tokenizer)
+        return;
     if (context.deferredCSSParserEnabled && !wrapper && styleSheet && ruleParsing == CSSParser::RuleParsing::Deferred)
         m_deferredParser = CSSDeferredParser::create(context, string, *styleSheet);
 }
index c71f0fe..f0daea2 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright 2014 The Chromium Authors. All rights reserved.
-// Copyright (C) 2016 Apple Inc. All rights reserved.
+// Copyright (C) 2016-2020 Apple Inc. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
index 4dbbdfe..1a93776 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright 2015 The Chromium Authors. All rights reserved.
-// Copyright (C) 2016 Apple Inc. All rights reserved.
+// Copyright (C) 2016-2020 Apple Inc. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
@@ -48,25 +48,53 @@ static String preprocessString(String string)
     return string.replace('\0', replacementCharacter);
 }
 
+std::unique_ptr<CSSTokenizer> CSSTokenizer::tryCreate(const String& string)
+{
+    bool success = true;
+    // We can't use makeUnique here because it does not have access to this private constructor.
+    auto tokenizer = std::unique_ptr<CSSTokenizer>(new CSSTokenizer(preprocessString(string), nullptr, &success));
+    if (UNLIKELY(!success))
+        return nullptr;
+    return tokenizer;
+}
+
+std::unique_ptr<CSSTokenizer> CSSTokenizer::tryCreate(const String& string, CSSParserObserverWrapper& wrapper)
+{
+    bool success = true;
+    // We can't use makeUnique here because it does not have access to this private constructor.
+    auto tokenizer = std::unique_ptr<CSSTokenizer>(new CSSTokenizer(preprocessString(string), &wrapper, &success));
+    if (UNLIKELY(!success))
+        return nullptr;
+    return tokenizer;
+}
+
 CSSTokenizer::CSSTokenizer(const String& string)
-    : CSSTokenizer(preprocessString(string), nullptr)
+    : CSSTokenizer(preprocessString(string), nullptr, nullptr)
 {
 }
 
 CSSTokenizer::CSSTokenizer(const String& string, CSSParserObserverWrapper& wrapper)
-    : CSSTokenizer(preprocessString(string), &wrapper)
+    : CSSTokenizer(preprocessString(string), &wrapper, nullptr)
 {
 }
 
-inline CSSTokenizer::CSSTokenizer(String&& string, CSSParserObserverWrapper* wrapper)
+CSSTokenizer::CSSTokenizer(String&& string, CSSParserObserverWrapper* wrapper, bool* constructionSuccessPtr)
     : m_input(string)
 {
+    if (constructionSuccessPtr)
+        *constructionSuccessPtr = true;
+
     if (string.isEmpty())
         return;
 
     // To avoid resizing we err on the side of reserving too much space.
     // Most strings we tokenize have about 3.5 to 5 characters per token.
-    m_tokens.reserveInitialCapacity(string.length() / 3);
+    if (UNLIKELY(!m_tokens.tryReserveInitialCapacity(string.length() / 3))) {
+        // When constructionSuccessPtr is null, our policy is to crash on failure.
+        RELEASE_ASSERT(constructionSuccessPtr);
+        *constructionSuccessPtr = false;
+        return;
+    }
 
     unsigned offset = 0;
     while (true) {
@@ -77,7 +105,12 @@ inline CSSTokenizer::CSSTokenizer(String&& string, CSSParserObserverWrapper* wra
             if (wrapper)
                 wrapper->addComment(offset, m_input.offset(), m_tokens.size());
         } else {
-            m_tokens.append(token);
+            if (UNLIKELY(!m_tokens.tryAppend(token))) {
+                // When constructionSuccessPtr is null, our policy is to crash on failure.
+                RELEASE_ASSERT(constructionSuccessPtr);
+                *constructionSuccessPtr = false;
+                return;
+            }
             if (wrapper)
                 wrapper->addToken(offset);
         }
index c7a05e5..26582d1 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright 2014 The Chromium Authors. All rights reserved.
-// Copyright (C) 2016 Apple Inc. All rights reserved.
+// Copyright (C) 2016-2020 Apple Inc. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
@@ -45,6 +45,9 @@ class CSSTokenizer {
     WTF_MAKE_NONCOPYABLE(CSSTokenizer);
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    static std::unique_ptr<CSSTokenizer> tryCreate(const String&);
+    static std::unique_ptr<CSSTokenizer> tryCreate(const String&, CSSParserObserverWrapper&); // For the inspector
+
     explicit CSSTokenizer(const String&);
     CSSTokenizer(const String&, CSSParserObserverWrapper&); // For the inspector
 
@@ -54,7 +57,7 @@ public:
     Vector<String>&& escapedStringsForAdoption() { return WTFMove(m_stringPool); }
 
 private:
-    CSSTokenizer(String&&, CSSParserObserverWrapper*);
+    CSSTokenizer(String&&, CSSParserObserverWrapper*, bool* constructionSuccess);
 
     CSSParserToken nextToken();