Make core/fxcrt/css compile under nounsafe_buffer_usage.

Change-Id: Ib7bc494cc688b077de12e2d6056cc51162c73c05
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/117030
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
diff --git a/core/fxcrt/css/BUILD.gn b/core/fxcrt/css/BUILD.gn
index cd138b6..3482f71 100644
--- a/core/fxcrt/css/BUILD.gn
+++ b/core/fxcrt/css/BUILD.gn
@@ -54,6 +54,7 @@
   configs += [
     "../../../:pdfium_strict_config",
     "../../../:pdfium_noshorten_config",
+    "../../../:pdfium_nounsafe_buffer_usage_config",
   ]
   deps = [
     "../",
diff --git a/core/fxcrt/css/cfx_cssdata.cpp b/core/fxcrt/css/cfx_cssdata.cpp
index 40aacf3..99c3e60 100644
--- a/core/fxcrt/css/cfx_cssdata.cpp
+++ b/core/fxcrt/css/cfx_cssdata.cpp
@@ -9,6 +9,8 @@
 #include <algorithm>
 #include <utility>
 
+#include "core/fxcrt/check_op.h"
+#include "core/fxcrt/compiler_specific.h"
 #include "core/fxcrt/css/cfx_cssstyleselector.h"
 #include "core/fxcrt/css/cfx_cssvaluelistparser.h"
 #include "core/fxcrt/fx_codepage.h"
@@ -73,7 +75,10 @@
 
 const CFX_CSSData::Property* CFX_CSSData::GetPropertyByEnum(
     CFX_CSSProperty property) {
-  return &kPropertyTable[static_cast<uint8_t>(property)];
+  auto index = static_cast<size_t>(property);
+  CHECK_LT(index, std::size(kPropertyTable));
+  // SAFETY: CHECK() on previous line ensures index is in bounds.
+  return UNSAFE_BUFFERS(&kPropertyTable[index]);
 }
 
 const CFX_CSSData::PropertyValue* CFX_CSSData::GetPropertyValueByName(
@@ -102,13 +107,12 @@
   WideString lowerName = WideString(wsName);
   lowerName.MakeLower();
 
-  for (auto* iter = std::begin(kLengthUnitTable);
-       iter != std::end(kLengthUnitTable); ++iter) {
-    if (lowerName == iter->value)
-      return iter;
-  }
-
-  return nullptr;
+  auto* iter =
+      std::find_if(std::begin(kLengthUnitTable), std::end(kLengthUnitTable),
+                   [lowerName](const CFX_CSSData::LengthUnit& unit) {
+                     return lowerName == unit.value;
+                   });
+  return iter != std::end(kLengthUnitTable) ? iter : nullptr;
 }
 
 const CFX_CSSData::Color* CFX_CSSData::GetColorByName(WideStringView wsName) {
@@ -118,10 +122,9 @@
   WideString lowerName = WideString(wsName);
   lowerName.MakeLower();
 
-  for (auto* iter = std::begin(kColorTable); iter != std::end(kColorTable);
-       ++iter) {
-    if (lowerName == iter->name)
-      return iter;
-  }
-  return nullptr;
+  auto* iter = std::find_if(std::begin(kColorTable), std::end(kColorTable),
+                            [lowerName](const CFX_CSSData::Color& color) {
+                              return lowerName == color.name;
+                            });
+  return iter != std::end(kColorTable) ? iter : nullptr;
 }
diff --git a/core/fxcrt/css/cfx_cssdeclaration.cpp b/core/fxcrt/css/cfx_cssdeclaration.cpp
index 87aee28..3e4f67a 100644
--- a/core/fxcrt/css/cfx_cssdeclaration.cpp
+++ b/core/fxcrt/css/cfx_cssdeclaration.cpp
@@ -8,6 +8,7 @@
 
 #include <math.h>
 
+#include <array>
 #include <utility>
 
 #include "core/fxcrt/check.h"
@@ -96,9 +97,9 @@
     if (!value.First(4).EqualsASCIINoCase("rgb(") || value.Back() != ')') {
       return std::nullopt;
     }
-    uint8_t rgb[3] = {0};
+    std::array<uint8_t, 3> rgb = {};
     CFX_CSSValueListParser list(value.Substr(4, value.GetLength() - 5), ',');
-    for (int32_t i = 0; i < 3; ++i) {
+    for (auto& component : rgb) {
       auto maybe_value = list.NextValue();
       if (!maybe_value.has_value() ||
           maybe_value.value().type != CFX_CSSValue::PrimitiveType::kNumber) {
@@ -108,9 +109,9 @@
       if (!maybe_number.has_value()) {
         return std::nullopt;
       }
-      rgb[i] = maybe_number.value().unit == CFX_CSSNumber::Unit::kPercent
-                   ? FXSYS_roundf(maybe_number.value().value * 2.55f)
-                   : FXSYS_roundf(maybe_number.value().value);
+      component = maybe_number.value().unit == CFX_CSSNumber::Unit::kPercent
+                      ? FXSYS_roundf(maybe_number.value().value * 2.55f)
+                      : FXSYS_roundf(maybe_number.value().value);
     }
     return ArgbEncode(255, rgb[0], rgb[1], rgb[2]);
   }
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.cpp b/core/fxcrt/css/cfx_cssvaluelistparser.cpp
index ac5471e..4fcd7be 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser.cpp
+++ b/core/fxcrt/css/cfx_cssvaluelistparser.cpp
@@ -8,29 +8,31 @@
 
 #include "core/fxcrt/check.h"
 #include "core/fxcrt/check_op.h"
+#include "core/fxcrt/compiler_specific.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_system.h"
 
 CFX_CSSValueListParser::CFX_CSSValueListParser(WideStringView list,
                                                wchar_t separator)
-    : m_Separator(separator),
-      m_pCur(list.unterminated_c_str()),
-      m_pEnd(list.unterminated_c_str() + list.GetLength()) {
-  DCHECK_NE(m_pCur, m_pEnd);
+    : m_Cur(list), m_Separator(separator) {
+  DCHECK(CharsRemain());
 }
 
+CFX_CSSValueListParser::~CFX_CSSValueListParser() = default;
+
 std::optional<CFX_CSSValueListParser::Result>
 CFX_CSSValueListParser::NextValue() {
-  while (m_pCur < m_pEnd && (*m_pCur <= ' ' || *m_pCur == m_Separator)) {
-    ++m_pCur;
+  while (CharsRemain() &&
+         (CurrentChar() <= ' ' || CurrentChar() == m_Separator)) {
+    Advance();
   }
-  if (m_pCur >= m_pEnd) {
+  if (!CharsRemain()) {
     return std::nullopt;
   }
   auto eType = CFX_CSSValue::PrimitiveType::kUnknown;
-  const wchar_t* pStart = m_pCur;
+  WideStringView start = m_Cur;
   size_t nLength = 0;
-  wchar_t wch = *m_pCur;
+  wchar_t wch = CurrentChar();
   if (wch == '#') {
     nLength = SkipToChar(' ');
     if (nLength == 4 || nLength == 7) {
@@ -38,57 +40,63 @@
     }
   } else if (FXSYS_IsDecimalDigit(wch) || wch == '.' || wch == '-' ||
              wch == '+') {
-    while (m_pCur < m_pEnd && (*m_pCur > ' ' && *m_pCur != m_Separator)) {
-      ++m_pCur;
+    while (CharsRemain() &&
+           (CurrentChar() > ' ' && CurrentChar() != m_Separator)) {
+      ++nLength;
+      Advance();
     }
-    nLength = m_pCur - pStart;
     eType = CFX_CSSValue::PrimitiveType::kNumber;
   } else if (wch == '\"' || wch == '\'') {
-    ++pStart;
-    ++m_pCur;
+    start = start.Substr(1);
+    Advance();
     nLength = SkipToChar(wch);
-    ++m_pCur;
+    Advance();
     eType = CFX_CSSValue::PrimitiveType::kString;
-  } else if (m_pEnd - m_pCur > 5 && m_pCur[3] == '(') {
-    if (FXSYS_wcsnicmp(L"rgb", m_pCur, 3) == 0) {
-      nLength = SkipToChar(')') + 1;
-      ++m_pCur;
-      eType = CFX_CSSValue::PrimitiveType::kRGB;
-    }
+  } else if (m_Cur.First(4).EqualsASCIINoCase(
+                 "rgb(")) {  // First() always safe.
+    nLength = SkipToChar(')') + 1;
+    Advance();
+    eType = CFX_CSSValue::PrimitiveType::kRGB;
   } else {
     nLength = SkipToCharMatchingParens(m_Separator);
     eType = CFX_CSSValue::PrimitiveType::kString;
   }
-  if (m_pCur <= m_pEnd && nLength > 0) {
-    return Result{eType, WideStringView(pStart, nLength)};
+  if (nLength == 0) {
+    return std::nullopt;
   }
-  return std::nullopt;
+  return Result{eType, start.First(nLength)};
 }
 
 size_t CFX_CSSValueListParser::SkipToChar(wchar_t wch) {
-  const wchar_t* pStart = m_pCur;
-  while (m_pCur < m_pEnd && *m_pCur != wch) {
-    m_pCur++;
+  size_t count = 0;
+  while (CharsRemain() && CurrentChar() != wch) {
+    Advance();
+    ++count;
   }
-  return m_pCur - pStart;
+  return count;
 }
 
 size_t CFX_CSSValueListParser::SkipToCharMatchingParens(wchar_t wch) {
-  const wchar_t* pStart = m_pCur;
+  size_t nLength = 0;
   int64_t bracketCount = 0;
-  while (m_pCur < m_pEnd && *m_pCur != wch) {
-    if (*m_pCur <= ' ')
+  while (CharsRemain() && CurrentChar() != wch) {
+    if (CurrentChar() <= ' ') {
       break;
-    if (*m_pCur == '(')
+    }
+    if (CurrentChar() == '(') {
       bracketCount++;
-    else if (*m_pCur == ')')
+    } else if (CurrentChar() == ')') {
       bracketCount--;
-    m_pCur++;
+    }
+    ++nLength;
+    Advance();
   }
-  while (bracketCount > 0 && m_pCur < m_pEnd) {
-    if (*m_pCur == ')')
+  while (bracketCount > 0 && CharsRemain()) {
+    if (CurrentChar() == ')') {
       bracketCount--;
-    m_pCur++;
+    }
+    ++nLength;
+    Advance();
   }
-  return m_pCur - pStart;
+  return nLength;
 }
diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.h b/core/fxcrt/css/cfx_cssvaluelistparser.h
index e6deb88..2673520 100644
--- a/core/fxcrt/css/cfx_cssvaluelistparser.h
+++ b/core/fxcrt/css/cfx_cssvaluelistparser.h
@@ -22,17 +22,25 @@
   };
 
   CFX_CSSValueListParser(WideStringView list, wchar_t separator);
+  ~CFX_CSSValueListParser();
 
   std::optional<Result> NextValue();
   void UseCommaSeparator() { m_Separator = ','; }
 
  private:
+  bool CharsRemain() const { return !m_Cur.IsEmpty(); }
+
+  // Safe to call even when input exhausted, stays unchanged.
+  void Advance() { m_Cur = m_Cur.Substr(1); }
+
+  // Safe to call even when input exhausted, returns NUL.
+  wchar_t CurrentChar() const { return static_cast<wchar_t>(m_Cur.Front()); }
+
   size_t SkipToChar(wchar_t wch);
   size_t SkipToCharMatchingParens(wchar_t wch);
 
+  WideStringView m_Cur;
   wchar_t m_Separator;
-  const wchar_t* m_pCur;
-  const wchar_t* m_pEnd;
 };
 
 #endif  // CORE_FXCRT_CSS_CFX_CSSVALUELISTPARSER_H_