Update pdfium::Contains() from Chromium

Now that C++20 is available, sync core/fxcrt/containers/contains.h with
Chromium's base//contains/contains.h at https://crrev.com/1414269, but
with a cleaner set of #includes. Then delete template_util.h, and fix
the build by using the Contains() overload that takes a projection.

Change-Id: I27d7a7a54402e6a6edc3a8d9f7e3fdc5502f731e
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/131252
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 3a0612f..796a10a 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -119,7 +119,6 @@
     "string_template.cpp",
     "string_template.h",
     "string_view_template.h",
-    "template_util.h",
     "tree_node.h",
     "unowned_ptr.h",
     "utf16.h",
diff --git a/core/fxcrt/containers/contains.h b/core/fxcrt/containers/contains.h
index e25f2f8..3175d49 100644
--- a/core/fxcrt/containers/contains.h
+++ b/core/fxcrt/containers/contains.h
@@ -5,108 +5,57 @@
 #ifndef CORE_FXCRT_CONTAINERS_CONTAINS_H_
 #define CORE_FXCRT_CONTAINERS_CONTAINS_H_
 
-#include <algorithm>
-#include <iterator>
-#include <type_traits>
+// This is Chromium's base/containers/unique_ptr_adapters.h, adapted to work
+// with PDFium's codebase with the following modifications:
+//
+// - Updated include guards.
+// - Replaced namespace base with namespace pdfium.
+// - Replaced <type_traits> with <concepts>.
 
-#include "core/fxcrt/template_util.h"
+// Provides `Contains()`, a general purpose utility to check whether a container
+// contains a value. This will probe whether a `contains` or `find` member
+// function on `container` exists, and fall back to a generic linear search over
+// `container`.
+
+#include <algorithm>
+#include <concepts>
+#include <ranges>
+#include <utility>
 
 namespace pdfium {
 
-namespace internal {
-
-// Small helper to detect whether a given type has a nested `key_type` typedef.
-// Used below to catch misuses of the API for associative containers.
-template <typename T, typename SFINAE = void>
-struct HasKeyType : std::false_type {};
-
-template <typename T>
-struct HasKeyType<T, std::void_t<typename T::key_type>> : std::true_type {};
-
-// Utility type traits used for specializing pdfium::Contains() below.
-template <typename Container, typename Element, typename = void>
-struct HasFindWithNpos : std::false_type {};
-
-template <typename Container, typename Element>
-struct HasFindWithNpos<
-    Container,
-    Element,
-    std::void_t<decltype(std::declval<const Container&>().find(
-                             std::declval<const Element&>()) !=
-                         Container::npos)>> : std::true_type {};
-
-template <typename Container, typename Element, typename = void>
-struct HasFindWithEnd : std::false_type {};
-
-template <typename Container, typename Element>
-struct HasFindWithEnd<
-    Container,
-    Element,
-    std::void_t<decltype(std::declval<const Container&>().find(
-                             std::declval<const Element&>()) !=
-                         std::declval<const Container&>().end())>>
-    : std::true_type {};
-
-template <typename Container, typename Element, typename = void>
-struct HasContains : std::false_type {};
-
-template <typename Container, typename Element>
-struct HasContains<
-    Container,
-    Element,
-    std::void_t<decltype(std::declval<const Container&>().contains(
-        std::declval<const Element&>()))>> : std::true_type {};
-
-}  // namespace internal
-
-// General purpose implementation to check if |container| contains |value|.
-template <typename Container,
-          typename Value,
-          std::enable_if_t<
-              !internal::HasFindWithNpos<Container, Value>::value &&
-              !internal::HasFindWithEnd<Container, Value>::value &&
-              !internal::HasContains<Container, Value>::value>* = nullptr>
-bool Contains(const Container& container, const Value& value) {
-  static_assert(
-      !internal::HasKeyType<Container>::value,
-      "Error: About to perform linear search on an associative container. "
-      "Either use a more generic comparator (e.g. std::less<>) or, if a linear "
-      "search is desired, provide an explicit projection parameter.");
-  using std::begin;
-  using std::end;
-  return std::find(begin(container), end(container), value) != end(container);
+// A general purpose utility to check whether `container` contains `value`. This
+// will probe whether a `contains` or `find` member function on `container`
+// exists, and fall back to a generic linear search over `container`.
+template <typename Container, typename Value>
+constexpr bool Contains(const Container& container, const Value& value) {
+  if constexpr (requires {
+                  { container.contains(value) } -> std::same_as<bool>;
+                }) {
+    return container.contains(value);
+  } else if constexpr (requires { container.find(value) != Container::npos; }) {
+    return container.find(value) != Container::npos;
+  } else if constexpr (requires { container.find(value) != container.end(); }) {
+    return container.find(value) != container.end();
+  } else {
+    static_assert(
+        !requires { typename Container::key_type; },
+        "Error: About to perform linear search on an associative container. "
+        "Either use a more generic comparator (e.g. std::less<>) or, if a "
+        "linear search is desired, provide an explicit projection parameter.");
+    return std::ranges::find(container, value) != std::ranges::end(container);
+  }
 }
 
-// Specialized Contains() implementation for when |container| has a find()
-// member function and a static npos member, but no contains() member function.
-template <typename Container,
-          typename Value,
-          std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
-                           !internal::HasContains<Container, Value>::value>* =
-              nullptr>
-bool Contains(const Container& container, const Value& value) {
-  return container.find(value) != Container::npos;
-}
-
-// Specialized Contains() implementation for when |container| has a find()
-// and end() member function, but no contains() member function.
-template <typename Container,
-          typename Value,
-          std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
-                           !internal::HasContains<Container, Value>::value>* =
-              nullptr>
-bool Contains(const Container& container, const Value& value) {
-  return container.find(value) != container.end();
-}
-
-// Specialized Contains() implementation for when |container| has a contains()
-// member function.
-template <
-    typename Container,
-    typename Value,
-    std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
-bool Contains(const Container& container, const Value& value) {
-  return container.contains(value);
+// Overload that allows callers to provide an additional projection invocable.
+// This projection will be applied to every element in `container` before
+// comparing it with `value`. This will always perform a linear search.
+template <typename Container, typename Value, typename Proj>
+constexpr bool Contains(const Container& container,
+                        const Value& value,
+                        Proj proj) {
+  return std::ranges::find(container, value, std::move(proj)) !=
+         std::ranges::end(container);
 }
 
 }  // namespace pdfium
diff --git a/core/fxcrt/template_util.h b/core/fxcrt/template_util.h
deleted file mode 100644
index a6abcb0..0000000
--- a/core/fxcrt/template_util.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2024 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CORE_FXCRT_TEMPLATE_UTIL_H_
-#define CORE_FXCRT_TEMPLATE_UTIL_H_
-
-#include <stddef.h>
-#include <iosfwd>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-
-#include "build/build_config.h"
-
-namespace pdfium {
-
-template <class T>
-struct is_non_const_reference : std::false_type {};
-template <class T>
-struct is_non_const_reference<T&> : std::true_type {};
-template <class T>
-struct is_non_const_reference<const T&> : std::false_type {};
-
-namespace internal {
-
-// Uses expression SFINAE to detect whether using operator<< would work.
-template <typename T, typename = void>
-struct SupportsOstreamOperator : std::false_type {};
-template <typename T>
-struct SupportsOstreamOperator<T,
-                               decltype(void(std::declval<std::ostream&>()
-                                             << std::declval<T>()))>
-    : std::true_type {};
-
-// Used to detech whether the given type is an iterator.  This is normally used
-// with std::enable_if to provide disambiguation for functions that take
-// templatzed iterators as input.
-template <typename T, typename = void>
-struct is_iterator : std::false_type {};
-
-template <typename T>
-struct is_iterator<
-    T,
-    std::void_t<typename std::iterator_traits<T>::iterator_category>>
-    : std::true_type {};
-
-}  // namespace internal
-
-}  // namespace pdfium
-
-#endif  // CORE_FXCRT_TEMPLATE_UTIL_H_
diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp
index b1a589f..b1f9aac 100644
--- a/fxjs/xfa/cfxjse_engine.cpp
+++ b/fxjs/xfa/cfxjse_engine.cpp
@@ -941,7 +941,9 @@
 }
 
 void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) {
-  if (script_node_array_ && !pdfium::Contains(*script_node_array_, pNode)) {
+  if (script_node_array_ &&
+      !pdfium::Contains(*script_node_array_, pNode,
+                        &cppgc::Persistent<CXFA_Node>::Get)) {
     script_node_array_->emplace_back(pNode);
   }
 }