Make CPDF_ColorSpace fully retainable.

And remove CPDF_CountedColorSpace. Use an observed ptr for the
cache to clean itself when the last non-retaining reference is
removed.

Change-Id: I127a697243be92f1fc6a4bb0d7cf1742d559acab
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/54597
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index 6238346..975b3c9 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -9,6 +9,7 @@
 
 #include "core/fpdfapi/cpdf_modulemgr.h"
 #include "core/fpdfapi/font/cpdf_font.h"
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
@@ -76,8 +77,7 @@
 
 TEST_F(CPDF_PageContentGeneratorTest, BUG_937) {
   static const std::vector<float> rgb = {0.000000000000000000001f, 0.7f, 0.35f};
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
-
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   {
     auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
     pPathObj->set_filltype(FXFILL_WINDING);
@@ -188,7 +188,7 @@
   pPathObj->path().AppendPoint(CFX_PointF(5, 6), FXPT_TYPE::LineTo, true);
 
   static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   pPathObj->m_ColorState.SetFillColor(pCS, rgb);
 
   static const std::vector<float> rgb2 = {1, 0.9f, 0};
@@ -245,7 +245,7 @@
   pTextObj->m_TextState.SetFont(pFont);
   pTextObj->m_TextState.SetFontSize(10.0f);
   static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   pTextObj->m_ColorState.SetFillColor(pCS, rgb);
 
   static const std::vector<float> rgb2 = {1, 0.9f, 0};
diff --git a/core/fpdfapi/page/cpdf_color.cpp b/core/fpdfapi/page/cpdf_color.cpp
index d772deb..6fbfae4 100644
--- a/core/fpdfapi/page/cpdf_color.cpp
+++ b/core/fpdfapi/page/cpdf_color.cpp
@@ -20,7 +20,6 @@
 
 CPDF_Color::~CPDF_Color() {
   ReleaseBuffer();
-  ReleaseColorSpace();
 }
 
 bool CPDF_Color::IsPattern() const {
@@ -45,38 +44,19 @@
   m_pBuffer = nullptr;
 }
 
-void CPDF_Color::ReleaseColorSpace() {
-  if (!m_pCS)
-    return;
-
-  CPDF_Document* pDoc = m_pCS->GetDocument();
-  if (!pDoc)
-    return;
-
-  auto* pPageData = pDoc->GetPageData();
-  if (pPageData)
-    pPageData->ReleaseColorSpace(m_pCS->GetArray());
-
-  m_pCS = nullptr;
-}
-
 bool CPDF_Color::IsPatternInternal() const {
   return m_pCS->GetFamily() == PDFCS_PATTERN;
 }
 
-void CPDF_Color::SetColorSpace(CPDF_ColorSpace* pCS) {
+void CPDF_Color::SetColorSpace(const RetainPtr<CPDF_ColorSpace>& pCS) {
   ASSERT(pCS);
   if (m_pCS == pCS) {
     if (!m_pBuffer)
       m_pBuffer = pCS->CreateBuf();
-
-    ReleaseColorSpace();
     m_pCS = pCS;
     return;
   }
   ReleaseBuffer();
-  ReleaseColorSpace();
-
   m_pCS = pCS;
   if (IsPatternInternal())
     m_pBuffer = pCS->CreateBuf();
@@ -128,7 +108,6 @@
     return *this;
 
   ReleaseBuffer();
-  ReleaseColorSpace();
   m_pCS = that.m_pCS;
   if (!m_pCS)
     return *this;
diff --git a/core/fpdfapi/page/cpdf_color.h b/core/fpdfapi/page/cpdf_color.h
index 084cc28..d3cfc94 100644
--- a/core/fpdfapi/page/cpdf_color.h
+++ b/core/fpdfapi/page/cpdf_color.h
@@ -9,9 +9,10 @@
 
 #include <vector>
 
-#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
+class CPDF_ColorSpace;
 class CPDF_Pattern;
 
 class CPDF_Color {
@@ -25,7 +26,7 @@
 
   bool IsNull() const { return !m_pBuffer; }
   bool IsPattern() const;
-  void SetColorSpace(CPDF_ColorSpace* pCS);
+  void SetColorSpace(const RetainPtr<CPDF_ColorSpace>& pCS);
   void SetValueForNonPattern(const std::vector<float>& values);
   void SetValueForPattern(CPDF_Pattern* pPattern,
                           const std::vector<float>& values);
@@ -38,14 +39,13 @@
 
  protected:
   void ReleaseBuffer();
-  void ReleaseColorSpace();
   bool IsPatternInternal() const;
 
   // TODO(thestig): Convert this to a smart pointer or vector.
   // |m_pBuffer| is created by |m_pCS|, so if it is non-null, then so is
-  // |m_pCS|.
+  // |m_pCS|, since SetColorSpace() prohibits setting to null.
   float* m_pBuffer = nullptr;
-  CPDF_ColorSpace* m_pCS = nullptr;
+  RetainPtr<CPDF_ColorSpace> m_pCS;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_COLOR_H_
diff --git a/core/fpdfapi/page/cpdf_colorspace.cpp b/core/fpdfapi/page/cpdf_colorspace.cpp
index 09a445d..cba3d26 100644
--- a/core/fpdfapi/page/cpdf_colorspace.cpp
+++ b/core/fpdfapi/page/cpdf_colorspace.cpp
@@ -216,12 +216,13 @@
                             const CPDF_Dictionary* pDict,
                             std::set<const CPDF_Object*>* pVisited,
                             uint32_t nExpectedComponents);
-  static CPDF_ColorSpace* GetStockAlternateProfile(uint32_t nComponents);
+  static RetainPtr<CPDF_ColorSpace> GetStockAlternateProfile(
+      uint32_t nComponents);
   static bool IsValidComponents(int32_t nComps);
   static std::vector<float> GetRanges(const CPDF_Dictionary* pDict,
                                       uint32_t nComponents);
 
-  MaybeOwned<CPDF_ColorSpace> m_pAlterCS;
+  RetainPtr<CPDF_ColorSpace> m_pAlterCS;
   RetainPtr<CPDF_IccProfile> m_pProfile;
   mutable std::vector<uint8_t> m_pCache;
   std::vector<float> m_pRanges;
@@ -241,8 +242,7 @@
   void EnableStdConversion(bool bEnabled) override;
 
  private:
-  CPDF_ColorSpace* m_pBaseCS = nullptr;
-  UnownedPtr<const CPDF_CountedColorSpace> m_pCountedBaseCS;
+  RetainPtr<CPDF_ColorSpace> m_pBaseCS;
   uint32_t m_nBaseComponents = 0;
   int m_MaxIndex = 0;
   ByteString m_Table;
@@ -266,9 +266,9 @@
   void EnableStdConversion(bool bEnabled) override;
 
  private:
-  std::unique_ptr<CPDF_ColorSpace> m_pAltCS;
-  std::unique_ptr<const CPDF_Function> m_pFunc;
   enum { None, All, Colorant } m_Type;
+  RetainPtr<CPDF_ColorSpace> m_pAltCS;
+  std::unique_ptr<const CPDF_Function> m_pFunc;
 };
 
 class CPDF_DeviceNCS final : public CPDF_ColorSpace {
@@ -288,7 +288,7 @@
   void EnableStdConversion(bool bEnabled) override;
 
  private:
-  std::unique_ptr<CPDF_ColorSpace> m_pAltCS;
+  RetainPtr<CPDF_ColorSpace> m_pAltCS;
   std::unique_ptr<const CPDF_Function> m_pFunc;
 };
 
@@ -417,7 +417,8 @@
 }  // namespace
 
 // static
-CPDF_ColorSpace* CPDF_ColorSpace::ColorspaceFromName(const ByteString& name) {
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::ColorspaceFromName(
+    const ByteString& name) {
   if (name == "DeviceRGB" || name == "RGB")
     return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   if (name == "DeviceGray" || name == "G")
@@ -430,19 +431,19 @@
 }
 
 // static
-CPDF_ColorSpace* CPDF_ColorSpace::GetStockCS(int family) {
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::GetStockCS(int family) {
   return CPDF_ModuleMgr::Get()->GetPageModule()->GetStockCS(family);
 }
 
 // static
-std::unique_ptr<CPDF_ColorSpace> CPDF_ColorSpace::Load(CPDF_Document* pDoc,
-                                                       CPDF_Object* pObj) {
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(CPDF_Document* pDoc,
+                                                 CPDF_Object* pObj) {
   std::set<const CPDF_Object*> visited;
   return Load(pDoc, pObj, &visited);
 }
 
 // static
-std::unique_ptr<CPDF_ColorSpace> CPDF_ColorSpace::Load(
+RetainPtr<CPDF_ColorSpace> CPDF_ColorSpace::Load(
     CPDF_Document* pDoc,
     const CPDF_Object* pObj,
     std::set<const CPDF_Object*>* pVisited) {
@@ -455,7 +456,7 @@
   pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pObj);
 
   if (pObj->IsName())
-    return pdfium::WrapUnique(ColorspaceFromName(pObj->GetString()));
+    return ColorspaceFromName(pObj->GetString());
 
   if (const CPDF_Stream* pStream = pObj->AsStream()) {
     const CPDF_Dictionary* pDict = pStream->GetDict();
@@ -464,12 +465,13 @@
 
     CPDF_DictionaryLocker locker(pDict);
     for (const auto& it : locker) {
-      std::unique_ptr<CPDF_ColorSpace> pRet;
-      CPDF_Object* pValue = it.second.Get();
-      if (ToName(pValue))
-        pRet.reset(ColorspaceFromName(pValue->GetString()));
-      if (pRet)
-        return pRet;
+      CPDF_Name* pValue = ToName(it.second.Get());
+      if (pValue) {
+        RetainPtr<CPDF_ColorSpace> pRet =
+            ColorspaceFromName(pValue->GetString());
+        if (pRet)
+          return pRet;
+      }
     }
     return nullptr;
   }
@@ -484,34 +486,34 @@
 
   ByteString familyname = pFamilyObj->GetString();
   if (pArray->size() == 1)
-    return pdfium::WrapUnique(ColorspaceFromName(familyname));
+    return ColorspaceFromName(familyname);
 
-  std::unique_ptr<CPDF_ColorSpace> pCS;
+  RetainPtr<CPDF_ColorSpace> pCS;
   switch (familyname.GetID()) {
     case FXBSTR_ID('C', 'a', 'l', 'G'):
-      pCS.reset(new CPDF_CalGray(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_CalGray>(pDoc);
       break;
     case FXBSTR_ID('C', 'a', 'l', 'R'):
-      pCS.reset(new CPDF_CalRGB(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_CalRGB>(pDoc);
       break;
     case FXBSTR_ID('L', 'a', 'b', 0):
-      pCS.reset(new CPDF_LabCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_LabCS>(pDoc);
       break;
     case FXBSTR_ID('I', 'C', 'C', 'B'):
-      pCS.reset(new CPDF_ICCBasedCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_ICCBasedCS>(pDoc);
       break;
     case FXBSTR_ID('I', 'n', 'd', 'e'):
     case FXBSTR_ID('I', 0, 0, 0):
-      pCS.reset(new CPDF_IndexedCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_IndexedCS>(pDoc);
       break;
     case FXBSTR_ID('S', 'e', 'p', 'a'):
-      pCS.reset(new CPDF_SeparationCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_SeparationCS>(pDoc);
       break;
     case FXBSTR_ID('D', 'e', 'v', 'i'):
-      pCS.reset(new CPDF_DeviceNCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_DeviceNCS>(pDoc);
       break;
     case FXBSTR_ID('P', 'a', 't', 't'):
-      pCS.reset(new CPDF_PatternCS(pDoc));
+      pCS = pdfium::MakeRetain<CPDF_PatternCS>(pDoc);
       break;
     default:
       return nullptr;
@@ -524,16 +526,6 @@
   return pCS;
 }
 
-void CPDF_ColorSpace::Release() {
-  if (this == GetStockCS(PDFCS_DEVICERGB) ||
-      this == GetStockCS(PDFCS_DEVICEGRAY) ||
-      this == GetStockCS(PDFCS_DEVICECMYK) ||
-      this == GetStockCS(PDFCS_PATTERN)) {
-    return;
-  }
-  delete this;
-}
-
 size_t CPDF_ColorSpace::GetBufSize() const {
   if (m_Family == PDFCS_PATTERN)
     return sizeof(PatternValue);
@@ -1049,7 +1041,7 @@
 }
 
 // static
-CPDF_ColorSpace* CPDF_ICCBasedCS::GetStockAlternateProfile(
+RetainPtr<CPDF_ColorSpace> CPDF_ICCBasedCS::GetStockAlternateProfile(
     uint32_t nComponents) {
   if (nComponents == 1)
     return GetStockCS(PDFCS_DEVICEGRAY);
@@ -1090,15 +1082,7 @@
 CPDF_IndexedCS::CPDF_IndexedCS(CPDF_Document* pDoc)
     : CPDF_ColorSpace(pDoc, PDFCS_INDEXED) {}
 
-CPDF_IndexedCS::~CPDF_IndexedCS() {
-  const CPDF_ColorSpace* pCS =
-      m_pCountedBaseCS ? m_pCountedBaseCS->get() : nullptr;
-  if (pCS && m_pDocument) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData)
-      pPageData->ReleaseColorSpace(pCS->GetArray());
-  }
-}
+CPDF_IndexedCS::~CPDF_IndexedCS() = default;
 
 uint32_t CPDF_IndexedCS::v_Load(CPDF_Document* pDoc,
                                 const CPDF_Array* pArray,
@@ -1121,7 +1105,6 @@
   if (family == PDFCS_INDEXED || family == PDFCS_PATTERN)
     return 0;
 
-  m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
   m_nBaseComponents = m_pBaseCS->CountComponents();
   m_pCompMinMax = pdfium::Vector2D<float>(m_nBaseComponents, 2);
   float defvalue;
diff --git a/core/fpdfapi/page/cpdf_colorspace.h b/core/fpdfapi/page/cpdf_colorspace.h
index 1789e21..a4827ba 100644
--- a/core/fpdfapi/page/cpdf_colorspace.h
+++ b/core/fpdfapi/page/cpdf_colorspace.h
@@ -13,6 +13,8 @@
 #include "core/fpdfapi/page/cpdf_pattern.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/observable.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 #define PDFCS_DEVICEGRAY 1
@@ -41,19 +43,17 @@
   float m_Comps[kMaxPatternColorComps];
 };
 
-class CPDF_ColorSpace {
+class CPDF_ColorSpace : public Retainable, public Observable<CPDF_ColorSpace> {
  public:
-  static CPDF_ColorSpace* GetStockCS(int Family);
-  static CPDF_ColorSpace* ColorspaceFromName(const ByteString& name);
-  static std::unique_ptr<CPDF_ColorSpace> Load(CPDF_Document* pDoc,
-                                               CPDF_Object* pCSObj);
-  static std::unique_ptr<CPDF_ColorSpace> Load(
+  static RetainPtr<CPDF_ColorSpace> GetStockCS(int Family);
+  static RetainPtr<CPDF_ColorSpace> ColorspaceFromName(const ByteString& name);
+  static RetainPtr<CPDF_ColorSpace> Load(CPDF_Document* pDoc,
+                                         CPDF_Object* pCSObj);
+  static RetainPtr<CPDF_ColorSpace> Load(
       CPDF_Document* pDoc,
       const CPDF_Object* pCSObj,
       std::set<const CPDF_Object*>* pVisited);
 
-  void Release();
-
   size_t GetBufSize() const;
   float* CreateBuf() const;
 
@@ -104,7 +104,7 @@
 
  protected:
   CPDF_ColorSpace(CPDF_Document* pDoc, int family);
-  virtual ~CPDF_ColorSpace();
+  ~CPDF_ColorSpace() override;
 
   // Returns the number of components, or 0 on failure.
   virtual uint32_t v_Load(CPDF_Document* pDoc,
@@ -123,20 +123,5 @@
  private:
   uint32_t m_nComponents = 0;
 };
-using CPDF_CountedColorSpace = CPDF_CountedObject<CPDF_ColorSpace>;
-
-namespace std {
-
-// Make std::unique_ptr<CPDF_ColorSpace> call Release() rather than
-// simply deleting the object.
-template <>
-struct default_delete<CPDF_ColorSpace> {
-  void operator()(CPDF_ColorSpace* pColorSpace) const {
-    if (pColorSpace)
-      pColorSpace->Release();
-  }
-};
-
-}  // namespace std
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_
diff --git a/core/fpdfapi/page/cpdf_colorstate.cpp b/core/fpdfapi/page/cpdf_colorstate.cpp
index c31ea14..41269fa 100644
--- a/core/fpdfapi/page/cpdf_colorstate.cpp
+++ b/core/fpdfapi/page/cpdf_colorstate.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_colorstate.h"
 
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_pattern.h"
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
 #include "core/fxge/fx_dib.h"
@@ -69,13 +70,13 @@
   return pColor && !pColor->IsNull();
 }
 
-void CPDF_ColorState::SetFillColor(CPDF_ColorSpace* pCS,
+void CPDF_ColorState::SetFillColor(const RetainPtr<CPDF_ColorSpace>& pCS,
                                    const std::vector<float>& values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
   SetColor(pCS, values, &pData->m_FillColor, &pData->m_FillColorRef);
 }
 
-void CPDF_ColorState::SetStrokeColor(CPDF_ColorSpace* pCS,
+void CPDF_ColorState::SetStrokeColor(const RetainPtr<CPDF_ColorSpace>& pCS,
                                      const std::vector<float>& values) {
   ColorData* pData = m_Ref.GetPrivateCopy();
   SetColor(pCS, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef);
@@ -93,7 +94,7 @@
   SetPattern(pPattern, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef);
 }
 
-void CPDF_ColorState::SetColor(CPDF_ColorSpace* pCS,
+void CPDF_ColorState::SetColor(const RetainPtr<CPDF_ColorSpace>& pCS,
                                const std::vector<float>& values,
                                CPDF_Color* color,
                                FX_COLORREF* colorref) {
diff --git a/core/fpdfapi/page/cpdf_colorstate.h b/core/fpdfapi/page/cpdf_colorstate.h
index 27f5eea..3b7c152 100644
--- a/core/fpdfapi/page/cpdf_colorstate.h
+++ b/core/fpdfapi/page/cpdf_colorstate.h
@@ -11,6 +11,7 @@
 
 #include "core/fpdfapi/page/cpdf_color.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxge/fx_dib.h"
 
@@ -41,8 +42,10 @@
   CPDF_Color* GetMutableStrokeColor();
   bool HasStrokeColor() const;
 
-  void SetFillColor(CPDF_ColorSpace* pCS, const std::vector<float>& values);
-  void SetStrokeColor(CPDF_ColorSpace* pCS, const std::vector<float>& values);
+  void SetFillColor(const RetainPtr<CPDF_ColorSpace>& pCS,
+                    const std::vector<float>& values);
+  void SetStrokeColor(const RetainPtr<CPDF_ColorSpace>& pCS,
+                      const std::vector<float>& values);
   void SetFillPattern(CPDF_Pattern* pattern, const std::vector<float>& values);
   void SetStrokePattern(CPDF_Pattern* pattern,
                         const std::vector<float>& values);
@@ -68,7 +71,7 @@
     ~ColorData() override;
   };
 
-  void SetColor(CPDF_ColorSpace* pCS,
+  void SetColor(const RetainPtr<CPDF_ColorSpace>& pCS,
                 const std::vector<float>& values,
                 CPDF_Color* color,
                 FX_COLORREF* colorref);
diff --git a/core/fpdfapi/page/cpdf_docpagedata.cpp b/core/fpdfapi/page/cpdf_docpagedata.cpp
index 51fd419..b311192 100644
--- a/core/fpdfapi/page/cpdf_docpagedata.cpp
+++ b/core/fpdfapi/page/cpdf_docpagedata.cpp
@@ -46,10 +46,6 @@
   for (auto& it : m_FontMap)
     delete it.second;
   m_FontMap.clear();
-
-  for (auto& it : m_ColorSpaceMap)
-    delete it.second;
-  m_ColorSpaceMap.clear();
 }
 
 void CPDF_DocPageData::Clear(bool bForceRelease) {
@@ -88,15 +84,7 @@
     }
   }
 
-  for (auto& it : m_ColorSpaceMap) {
-    CPDF_CountedColorSpace* csData = it.second;
-    if (!csData->get())
-      continue;
-    if (bForceRelease || csData->use_count() < 2) {
-      csData->get()->Release();
-      csData->reset(nullptr);
-    }
-  }
+  m_ColorSpaceMap.clear();
 
   for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end();) {
     auto curr_it = it++;
@@ -212,14 +200,14 @@
   pFontData->clear();
 }
 
-CPDF_ColorSpace* CPDF_DocPageData::GetColorSpace(
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpace(
     const CPDF_Object* pCSObj,
     const CPDF_Dictionary* pResources) {
   std::set<const CPDF_Object*> visited;
   return GetColorSpaceGuarded(pCSObj, pResources, &visited);
 }
 
-CPDF_ColorSpace* CPDF_DocPageData::GetColorSpaceGuarded(
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpaceGuarded(
     const CPDF_Object* pCSObj,
     const CPDF_Dictionary* pResources,
     std::set<const CPDF_Object*>* pVisited) {
@@ -227,7 +215,7 @@
   return GetColorSpaceInternal(pCSObj, pResources, pVisited, &visitedLocal);
 }
 
-CPDF_ColorSpace* CPDF_DocPageData::GetColorSpaceInternal(
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetColorSpaceInternal(
     const CPDF_Object* pCSObj,
     const CPDF_Dictionary* pResources,
     std::set<const CPDF_Object*>* pVisited,
@@ -243,7 +231,7 @@
 
   if (pCSObj->IsName()) {
     ByteString name = pCSObj->GetString();
-    CPDF_ColorSpace* pCS = CPDF_ColorSpace::ColorspaceFromName(name);
+    RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::ColorspaceFromName(name);
     if (!pCS && pResources) {
       const CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace");
       if (pList) {
@@ -286,60 +274,29 @@
                                  pVisited, pVisitedInternal);
   }
 
-  CPDF_CountedColorSpace* csData = nullptr;
   auto it = m_ColorSpaceMap.find(pCSObj);
-  if (it != m_ColorSpaceMap.end()) {
-    csData = it->second;
-    if (csData->get()) {
-      return csData->AddRef();
-    }
-  }
+  if (it != m_ColorSpaceMap.end() && it->second)
+    return pdfium::WrapRetain(it->second.Get());
 
-  std::unique_ptr<CPDF_ColorSpace> pCS =
+  RetainPtr<CPDF_ColorSpace> pCS =
       CPDF_ColorSpace::Load(m_pPDFDoc.Get(), pArray, pVisited);
   if (!pCS)
     return nullptr;
 
-  if (csData) {
-    csData->reset(std::move(pCS));
-  } else {
-    csData = new CPDF_CountedColorSpace(std::move(pCS));
-    m_ColorSpaceMap[pCSObj] = csData;
-  }
-  return csData->AddRef();
+  m_ColorSpaceMap[pCSObj].Reset(pCS.Get());
+  return pCS;
 }
 
-CPDF_ColorSpace* CPDF_DocPageData::GetCopiedColorSpace(
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::GetCopiedColorSpace(
     const CPDF_Object* pCSObj) {
   if (!pCSObj)
     return nullptr;
 
   auto it = m_ColorSpaceMap.find(pCSObj);
-  if (it != m_ColorSpaceMap.end())
-    return it->second->AddRef();
+  if (it == m_ColorSpaceMap.end() || !it->second)
+    return nullptr;
 
-  return nullptr;
-}
-
-void CPDF_DocPageData::ReleaseColorSpace(const CPDF_Object* pColorSpace) {
-  if (!pColorSpace)
-    return;
-
-  auto it = m_ColorSpaceMap.find(pColorSpace);
-  if (it == m_ColorSpaceMap.end())
-    return;
-
-  CPDF_CountedColorSpace* pCountedColorSpace = it->second;
-  if (!pCountedColorSpace->get())
-    return;
-
-  pCountedColorSpace->RemoveRef();
-  if (pCountedColorSpace->use_count() > 1)
-    return;
-
-  // We have item only in m_ColorSpaceMap cache. Clean it.
-  pCountedColorSpace->get()->Release();
-  pCountedColorSpace->reset(nullptr);
+  return pdfium::WrapRetain(it->second.Get());
 }
 
 CPDF_Pattern* CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj,
@@ -495,13 +452,16 @@
     m_FontFileMap.erase(it);
 }
 
-CPDF_CountedColorSpace* CPDF_DocPageData::FindColorSpacePtr(
+RetainPtr<CPDF_ColorSpace> CPDF_DocPageData::FindColorSpacePtr(
     const CPDF_Object* pCSObj) const {
   if (!pCSObj)
     return nullptr;
 
   auto it = m_ColorSpaceMap.find(pCSObj);
-  return it != m_ColorSpaceMap.end() ? it->second : nullptr;
+  if (it == m_ColorSpaceMap.end() || !it->second)
+    return nullptr;
+
+  return pdfium::WrapRetain(it->second.Get());
 }
 
 CPDF_CountedPattern* CPDF_DocPageData::FindPatternPtr(
diff --git a/core/fpdfapi/page/cpdf_docpagedata.h b/core/fpdfapi/page/cpdf_docpagedata.h
index a684896..ae3e8db 100644
--- a/core/fpdfapi/page/cpdf_docpagedata.h
+++ b/core/fpdfapi/page/cpdf_docpagedata.h
@@ -13,6 +13,7 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Dictionary;
@@ -40,18 +41,18 @@
   void ReleaseFont(const CPDF_Dictionary* pFontDict);
 
   // Loads a colorspace.
-  CPDF_ColorSpace* GetColorSpace(const CPDF_Object* pCSObj,
-                                 const CPDF_Dictionary* pResources);
+  RetainPtr<CPDF_ColorSpace> GetColorSpace(const CPDF_Object* pCSObj,
+                                           const CPDF_Dictionary* pResources);
 
   // Loads a colorspace in a context that might be while loading another
   // colorspace. |pVisited| is passed recursively to avoid circular calls
   // involving CPDF_ColorSpace::Load().
-  CPDF_ColorSpace* GetColorSpaceGuarded(const CPDF_Object* pCSObj,
-                                        const CPDF_Dictionary* pResources,
-                                        std::set<const CPDF_Object*>* pVisited);
+  RetainPtr<CPDF_ColorSpace> GetColorSpaceGuarded(
+      const CPDF_Object* pCSObj,
+      const CPDF_Dictionary* pResources,
+      std::set<const CPDF_Object*>* pVisited);
 
-  CPDF_ColorSpace* GetCopiedColorSpace(const CPDF_Object* pCSObj);
-  void ReleaseColorSpace(const CPDF_Object* pColorSpace);
+  RetainPtr<CPDF_ColorSpace> GetCopiedColorSpace(const CPDF_Object* pCSObj);
 
   CPDF_Pattern* GetPattern(CPDF_Object* pPatternObj,
                            bool bShading,
@@ -68,7 +69,7 @@
       const CPDF_Stream* pFontStream);
   void MaybePurgeFontFileStreamAcc(const CPDF_Stream* pFontStream);
 
-  CPDF_CountedColorSpace* FindColorSpacePtr(const CPDF_Object* pCSObj) const;
+  RetainPtr<CPDF_ColorSpace> FindColorSpacePtr(const CPDF_Object* pCSObj) const;
   CPDF_CountedPattern* FindPatternPtr(const CPDF_Object* pPatternObj) const;
 
  private:
@@ -79,7 +80,7 @@
   // is passed recursively to avoid circular calls involving
   // CPDF_ColorSpace::Load() and |pVisitedInternal| is also passed recursively
   // to avoid circular calls with this method calling itself.
-  CPDF_ColorSpace* GetColorSpaceInternal(
+  RetainPtr<CPDF_ColorSpace> GetColorSpaceInternal(
       const CPDF_Object* pCSObj,
       const CPDF_Dictionary* pResources,
       std::set<const CPDF_Object*>* pVisited,
@@ -88,7 +89,7 @@
   bool m_bForceClear;
   UnownedPtr<CPDF_Document> const m_pPDFDoc;
   std::map<ByteString, const CPDF_Stream*> m_HashProfileMap;
-  std::map<const CPDF_Object*, CPDF_CountedColorSpace*> m_ColorSpaceMap;
+  std::map<const CPDF_Object*, CPDF_ColorSpace::ObservedPtr> m_ColorSpaceMap;
   std::map<const CPDF_Stream*, RetainPtr<CPDF_StreamAcc>> m_FontFileMap;
   std::map<const CPDF_Dictionary*, CPDF_CountedFont*> m_FontMap;
   std::map<const CPDF_Stream*, RetainPtr<CPDF_IccProfile>> m_IccProfileMap;
diff --git a/core/fpdfapi/page/cpdf_meshstream.cpp b/core/fpdfapi/page/cpdf_meshstream.cpp
index f99ae6e..2e80fb9 100644
--- a/core/fpdfapi/page/cpdf_meshstream.cpp
+++ b/core/fpdfapi/page/cpdf_meshstream.cpp
@@ -98,7 +98,7 @@
     ShadingType type,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
     const CPDF_Stream* pShadingStream,
-    const CPDF_ColorSpace* pCS)
+    const RetainPtr<CPDF_ColorSpace>& pCS)
     : m_type(type),
       m_funcs(funcs),
       m_pShadingStream(pShadingStream),
diff --git a/core/fpdfapi/page/cpdf_meshstream.h b/core/fpdfapi/page/cpdf_meshstream.h
index 6506e19..58805cd 100644
--- a/core/fpdfapi/page/cpdf_meshstream.h
+++ b/core/fpdfapi/page/cpdf_meshstream.h
@@ -14,6 +14,7 @@
 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
 #include "core/fxcrt/cfx_bitstream.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_StreamAcc;
 
@@ -39,7 +40,7 @@
   CPDF_MeshStream(ShadingType type,
                   const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
                   const CPDF_Stream* pShadingStream,
-                  const CPDF_ColorSpace* pCS);
+                  const RetainPtr<CPDF_ColorSpace>& pCS);
   ~CPDF_MeshStream();
 
   bool Load();
@@ -68,7 +69,7 @@
   const ShadingType m_type;
   const std::vector<std::unique_ptr<CPDF_Function>>& m_funcs;
   UnownedPtr<const CPDF_Stream> const m_pShadingStream;
-  UnownedPtr<const CPDF_ColorSpace> const m_pCS;
+  RetainPtr<CPDF_ColorSpace> const m_pCS;
   uint32_t m_nCoordBits;
   uint32_t m_nComponentBits;
   uint32_t m_nFlagBits;
diff --git a/core/fpdfapi/page/cpdf_pagemodule.cpp b/core/fpdfapi/page/cpdf_pagemodule.cpp
index 3b18ace..adc7793 100644
--- a/core/fpdfapi/page/cpdf_pagemodule.cpp
+++ b/core/fpdfapi/page/cpdf_pagemodule.cpp
@@ -6,29 +6,33 @@
 
 #include "core/fpdfapi/page/cpdf_pagemodule.h"
 
+#include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fpdfapi/page/cpdf_devicecs.h"
+#include "core/fpdfapi/page/cpdf_patterncs.h"
+
 CPDF_PageModule::CPDF_PageModule()
-    : m_StockGrayCS(PDFCS_DEVICEGRAY),
-      m_StockRGBCS(PDFCS_DEVICERGB),
-      m_StockCMYKCS(PDFCS_DEVICECMYK),
-      m_StockPatternCS(nullptr) {
-  m_StockPatternCS.InitializeStockPattern();
+    : m_StockGrayCS(pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICEGRAY)),
+      m_StockRGBCS(pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICERGB)),
+      m_StockCMYKCS(pdfium::MakeRetain<CPDF_DeviceCS>(PDFCS_DEVICECMYK)),
+      m_StockPatternCS(pdfium::MakeRetain<CPDF_PatternCS>(nullptr)) {
+  m_StockPatternCS->InitializeStockPattern();
 }
 
-CPDF_PageModule::~CPDF_PageModule() {}
+CPDF_PageModule::~CPDF_PageModule() = default;
 
 CPDF_FontGlobals* CPDF_PageModule::GetFontGlobals() {
   return &m_FontGlobals;
 }
 
-CPDF_ColorSpace* CPDF_PageModule::GetStockCS(int family) {
+RetainPtr<CPDF_ColorSpace> CPDF_PageModule::GetStockCS(int family) {
   if (family == PDFCS_DEVICEGRAY)
-    return &m_StockGrayCS;
+    return m_StockGrayCS;
   if (family == PDFCS_DEVICERGB)
-    return &m_StockRGBCS;
+    return m_StockRGBCS;
   if (family == PDFCS_DEVICECMYK)
-    return &m_StockCMYKCS;
+    return m_StockCMYKCS;
   if (family == PDFCS_PATTERN)
-    return &m_StockPatternCS;
+    return m_StockPatternCS;
   return nullptr;
 }
 
diff --git a/core/fpdfapi/page/cpdf_pagemodule.h b/core/fpdfapi/page/cpdf_pagemodule.h
index 720ee85..ae6363d 100644
--- a/core/fpdfapi/page/cpdf_pagemodule.h
+++ b/core/fpdfapi/page/cpdf_pagemodule.h
@@ -8,11 +8,12 @@
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
 
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
-#include "core/fpdfapi/page/cpdf_colorspace.h"
-#include "core/fpdfapi/page/cpdf_devicecs.h"
-#include "core/fpdfapi/page/cpdf_patterncs.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
+class CPDF_ColorSpace;
+class CPDF_DeviceCS;
+class CPDF_PatternCS;
 
 class CPDF_PageModule {
  public:
@@ -20,15 +21,15 @@
   ~CPDF_PageModule();
 
   CPDF_FontGlobals* GetFontGlobals();
-  CPDF_ColorSpace* GetStockCS(int family);
+  RetainPtr<CPDF_ColorSpace> GetStockCS(int family);
   void ClearStockFont(CPDF_Document* pDoc);
 
  private:
   CPDF_FontGlobals m_FontGlobals;
-  CPDF_DeviceCS m_StockGrayCS;
-  CPDF_DeviceCS m_StockRGBCS;
-  CPDF_DeviceCS m_StockCMYKCS;
-  CPDF_PatternCS m_StockPatternCS;
+  RetainPtr<CPDF_DeviceCS> m_StockGrayCS;
+  RetainPtr<CPDF_DeviceCS> m_StockRGBCS;
+  RetainPtr<CPDF_DeviceCS> m_StockCMYKCS;
+  RetainPtr<CPDF_PatternCS> m_StockPatternCS;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_
diff --git a/core/fpdfapi/page/cpdf_patterncs.cpp b/core/fpdfapi/page/cpdf_patterncs.cpp
index 69be3d2..312c9e9 100644
--- a/core/fpdfapi/page/cpdf_patterncs.cpp
+++ b/core/fpdfapi/page/cpdf_patterncs.cpp
@@ -11,19 +11,9 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 
 CPDF_PatternCS::CPDF_PatternCS(CPDF_Document* pDoc)
-    : CPDF_ColorSpace(pDoc, PDFCS_PATTERN),
-      m_pBaseCS(nullptr),
-      m_pCountedBaseCS(nullptr) {}
+    : CPDF_ColorSpace(pDoc, PDFCS_PATTERN) {}
 
-CPDF_PatternCS::~CPDF_PatternCS() {
-  const CPDF_ColorSpace* pCS =
-      m_pCountedBaseCS ? m_pCountedBaseCS->get() : nullptr;
-  if (pCS && m_pDocument) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData)
-      pPageData->ReleaseColorSpace(pCS->GetArray());
-  }
-}
+CPDF_PatternCS::~CPDF_PatternCS() = default;
 
 void CPDF_PatternCS::InitializeStockPattern() {
   SetComponentsForStockCS(1);
@@ -44,7 +34,6 @@
   if (m_pBaseCS->GetFamily() == PDFCS_PATTERN)
     return 0;
 
-  m_pCountedBaseCS = pDocPageData->FindColorSpacePtr(m_pBaseCS->GetArray());
   if (m_pBaseCS->CountComponents() > kMaxPatternColorComps)
     return 0;
 
diff --git a/core/fpdfapi/page/cpdf_patterncs.h b/core/fpdfapi/page/cpdf_patterncs.h
index 5de9875..e0e2a8a 100644
--- a/core/fpdfapi/page/cpdf_patterncs.h
+++ b/core/fpdfapi/page/cpdf_patterncs.h
@@ -10,6 +10,7 @@
 #include <set>
 
 #include "core/fpdfapi/page/cpdf_colorspace.h"
+#include "core/fxcrt/retain_ptr.h"
 
 class CPDF_Document;
 
@@ -35,8 +36,7 @@
                      float* B) const override;
 
  private:
-  const CPDF_ColorSpace* m_pBaseCS;
-  const CPDF_CountedColorSpace* m_pCountedBaseCS;
+  RetainPtr<CPDF_ColorSpace> m_pBaseCS;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PATTERNCS_H_
diff --git a/core/fpdfapi/page/cpdf_shadingpattern.cpp b/core/fpdfapi/page/cpdf_shadingpattern.cpp
index 0a6f4c6..b2d3957 100644
--- a/core/fpdfapi/page/cpdf_shadingpattern.cpp
+++ b/core/fpdfapi/page/cpdf_shadingpattern.cpp
@@ -36,16 +36,7 @@
     SetPatternToFormMatrix();
 }
 
-CPDF_ShadingPattern::~CPDF_ShadingPattern() {
-  CPDF_ColorSpace* pCountedCS = m_pCountedCS ? m_pCountedCS->get() : nullptr;
-  if (pCountedCS) {
-    auto* pPageData = document()->GetPageData();
-    if (pPageData) {
-      m_pCS.Release();  // Give up unowned reference first.
-      pPageData->ReleaseColorSpace(pCountedCS->GetArray());
-    }
-  }
-}
+CPDF_ShadingPattern::~CPDF_ShadingPattern() = default;
 
 CPDF_TilingPattern* CPDF_ShadingPattern::AsTilingPattern() {
   return nullptr;
@@ -88,7 +79,6 @@
   if (!m_pCS || m_pCS->GetFamily() == PDFCS_PATTERN)
     return false;
 
-  m_pCountedCS = pDocPageData->FindColorSpacePtr(m_pCS->GetArray());
   m_ShadingType = ToShadingType(pShadingDict->GetIntegerFor("ShadingType"));
   return Validate();
 }
diff --git a/core/fpdfapi/page/cpdf_shadingpattern.h b/core/fpdfapi/page/cpdf_shadingpattern.h
index cce1e04..2d8fc8e 100644
--- a/core/fpdfapi/page/cpdf_shadingpattern.h
+++ b/core/fpdfapi/page/cpdf_shadingpattern.h
@@ -13,6 +13,7 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_pattern.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 // Values used in PDFs except for |kInvalidShading| and |kMaxShading|.
@@ -57,7 +58,7 @@
   ShadingType GetShadingType() const { return m_ShadingType; }
   bool IsShadingObject() const { return m_bShading; }
   const CPDF_Object* GetShadingObject() const;
-  const CPDF_ColorSpace* GetCS() const { return m_pCS.Get(); }
+  RetainPtr<CPDF_ColorSpace> GetCS() const { return m_pCS; }
   const std::vector<std::unique_ptr<CPDF_Function>>& GetFuncs() const {
     return m_pFunctions;
   }
@@ -71,12 +72,7 @@
 
   ShadingType m_ShadingType = kInvalidShading;
   const bool m_bShading;
-
-  // Still keep |m_pCS| as some CPDF_ColorSpace (name object) are not managed
-  // as counted objects. Refer to CPDF_DocPageData::GetColorSpace.
-  UnownedPtr<const CPDF_ColorSpace> m_pCS;
-
-  UnownedPtr<const CPDF_CountedColorSpace> m_pCountedCS;
+  RetainPtr<CPDF_ColorSpace> m_pCS;
   std::vector<std::unique_ptr<CPDF_Function>> m_pFunctions;
 };
 
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index 2538984..8b5b10c 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -76,7 +76,7 @@
                              const CFX_Matrix& matrix) {
   ShadingType type = pShading->GetShadingType();
   const CPDF_Stream* pStream = ToStream(pShading->GetShadingObject());
-  const CPDF_ColorSpace* pCS = pShading->GetCS();
+  RetainPtr<CPDF_ColorSpace> pCS = pShading->GetCS();
   if (!pStream || !pCS)
     return CFX_FloatRect();
 
@@ -698,7 +698,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetColorSpace_Fill() {
-  CPDF_ColorSpace* pCS = FindColorSpace(GetString(0));
+  RetainPtr<CPDF_ColorSpace> pCS = FindColorSpace(GetString(0));
   if (!pCS)
     return;
 
@@ -706,7 +706,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetColorSpace_Stroke() {
-  CPDF_ColorSpace* pCS = FindColorSpace(GetString(0));
+  RetainPtr<CPDF_ColorSpace> pCS = FindColorSpace(GetString(0));
   if (!pCS)
     return;
 
@@ -892,12 +892,14 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetGray_Fill() {
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
   m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(1));
 }
 
 void CPDF_StreamContentParser::Handle_SetGray_Stroke() {
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY);
   m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(1));
 }
 
@@ -941,7 +943,8 @@
   if (m_ParamCount != 4)
     return;
 
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
   m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(4));
 }
 
@@ -949,7 +952,8 @@
   if (m_ParamCount != 4)
     return;
 
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+  RetainPtr<CPDF_ColorSpace> pCS =
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
   m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(4));
 }
 
@@ -1010,7 +1014,7 @@
   if (m_ParamCount != 3)
     return;
 
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(3));
 }
 
@@ -1018,7 +1022,7 @@
   if (m_ParamCount != 3)
     return;
 
-  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  RetainPtr<CPDF_ColorSpace> pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
   m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(3));
 }
 
@@ -1164,7 +1168,7 @@
   return pFont;
 }
 
-CPDF_ColorSpace* CPDF_StreamContentParser::FindColorSpace(
+RetainPtr<CPDF_ColorSpace> CPDF_StreamContentParser::FindColorSpace(
     const ByteString& name) {
   if (name == "Pattern") {
     return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN);
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h
index b6d36b7..0af1922 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.h
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.h
@@ -16,6 +16,7 @@
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
 #include "core/fxcrt/fx_number.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_pathdata.h"
 
 class CPDF_AllStates;
@@ -116,7 +117,7 @@
                         bool bColor,
                         bool bText,
                         bool bGraph);
-  CPDF_ColorSpace* FindColorSpace(const ByteString& name);
+  RetainPtr<CPDF_ColorSpace> FindColorSpace(const ByteString& name);
   CPDF_Pattern* FindPattern(const ByteString& name, bool bShading);
   CPDF_Dictionary* FindResourceHolder(const ByteString& type);
   CPDF_Object* FindResourceObj(const ByteString& type, const ByteString& name);
diff --git a/core/fpdfapi/page/cpdf_streamparser.cpp b/core/fpdfapi/page/cpdf_streamparser.cpp
index 88336f4..f7f18c1 100644
--- a/core/fpdfapi/page/cpdf_streamparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser.cpp
@@ -156,14 +156,9 @@
   uint32_t bpc = 1;
   uint32_t nComponents = 1;
   if (pCSObj) {
+    RetainPtr<CPDF_ColorSpace> pCS = pDoc->LoadColorSpace(pCSObj, nullptr);
+    nComponents = pCS ? pCS->CountComponents() : 3;
     bpc = pDict->GetIntegerFor("BitsPerComponent");
-    CPDF_ColorSpace* pCS = pDoc->LoadColorSpace(pCSObj, nullptr);
-    if (pCS) {
-      nComponents = pCS->CountComponents();
-      pDoc->GetPageData()->ReleaseColorSpace(pCSObj);
-    } else {
-      nComponents = 3;
-    }
   }
   FX_SAFE_UINT32 size = CalculatePitch8(bpc, nComponents, width);
   size *= height;
diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp
index be04432..417bfd6 100644
--- a/core/fpdfapi/parser/cpdf_document.cpp
+++ b/core/fpdfapi/parser/cpdf_document.cpp
@@ -475,7 +475,7 @@
   return m_pDocPage->GetFontFileStreamAcc(pStream);
 }
 
-CPDF_ColorSpace* CPDF_Document::LoadColorSpace(
+RetainPtr<CPDF_ColorSpace> CPDF_Document::LoadColorSpace(
     const CPDF_Object* pCSObj,
     const CPDF_Dictionary* pResources) {
   return m_pDocPage->GetColorSpace(pCSObj, pResources);
diff --git a/core/fpdfapi/parser/cpdf_document.h b/core/fpdfapi/parser/cpdf_document.h
index 5e1a28f..6684eb8 100644
--- a/core/fpdfapi/parser/cpdf_document.h
+++ b/core/fpdfapi/parser/cpdf_document.h
@@ -91,8 +91,8 @@
 
   // |pFontDict| must not be null.
   CPDF_Font* LoadFont(CPDF_Dictionary* pFontDict);
-  CPDF_ColorSpace* LoadColorSpace(const CPDF_Object* pCSObj,
-                                  const CPDF_Dictionary* pResources);
+  RetainPtr<CPDF_ColorSpace> LoadColorSpace(const CPDF_Object* pCSObj,
+                                            const CPDF_Dictionary* pResources);
 
   CPDF_Pattern* LoadPattern(CPDF_Object* pObj,
                             bool bShading,
diff --git a/core/fpdfapi/render/cpdf_dibbase.cpp b/core/fpdfapi/render/cpdf_dibbase.cpp
index 4441023..c14d05f 100644
--- a/core/fpdfapi/render/cpdf_dibbase.cpp
+++ b/core/fpdfapi/render/cpdf_dibbase.cpp
@@ -109,17 +109,9 @@
 
 }  // namespace
 
-CPDF_DIBBase::CPDF_DIBBase() {}
+CPDF_DIBBase::CPDF_DIBBase() = default;
 
-CPDF_DIBBase::~CPDF_DIBBase() {
-  if (m_pColorSpace && m_pDocument) {
-    auto* pPageData = m_pDocument->GetPageData();
-    if (pPageData) {
-      auto* pSpace = m_pColorSpace.Release();
-      pPageData->ReleaseColorSpace(pSpace->GetArray());
-    }
-  }
-}
+CPDF_DIBBase::~CPDF_DIBBase() = default;
 
 bool CPDF_DIBBase::Load(CPDF_Document* pDoc, const CPDF_Stream* pStream) {
   if (!pStream)
@@ -596,7 +588,7 @@
   CCodec_JpxModule* pJpxModule = CPDF_ModuleMgr::Get()->GetJpxModule();
   auto context = pdfium::MakeUnique<JpxBitMapContext>(pJpxModule);
   context->set_decoder(
-      pJpxModule->CreateDecoder(m_pStreamAcc->GetSpan(), m_pColorSpace.Get()));
+      pJpxModule->CreateDecoder(m_pStreamAcc->GetSpan(), m_pColorSpace));
   if (!context->decoder())
     return nullptr;
 
diff --git a/core/fpdfapi/render/cpdf_dibbase.h b/core/fpdfapi/render/cpdf_dibbase.h
index f23b7a1..df9e41f 100644
--- a/core/fpdfapi/render/cpdf_dibbase.h
+++ b/core/fpdfapi/render/cpdf_dibbase.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_clippath.h"
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_countedobject.h"
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 #include "core/fpdfapi/render/cpdf_imageloader.h"
@@ -58,7 +59,7 @@
                           int clip_left,
                           int clip_width) const override;
 
-  const CPDF_ColorSpace* GetColorSpace() const { return m_pColorSpace.Get(); }
+  RetainPtr<CPDF_ColorSpace> GetColorSpace() const { return m_pColorSpace; }
   uint32_t GetMatteColor() const { return m_MatteColor; }
 
   LoadState StartLoadDIBBase(CPDF_Document* pDoc,
@@ -128,7 +129,7 @@
   UnownedPtr<const CPDF_Stream> m_pStream;
   UnownedPtr<const CPDF_Dictionary> m_pDict;
   RetainPtr<CPDF_StreamAcc> m_pStreamAcc;
-  UnownedPtr<CPDF_ColorSpace> m_pColorSpace;
+  RetainPtr<CPDF_ColorSpace> m_pColorSpace;
   uint32_t m_Family = 0;
   uint32_t m_bpc = 0;
   uint32_t m_bpc_orig = 0;
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp
index 877a907..183823e 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp
+++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp
@@ -147,16 +147,15 @@
   CPDF_Object* pCSObj =
       m_pImageObject->GetImage()->GetStream()->GetDict()->GetDirectObjectFor(
           "ColorSpace");
-  CPDF_ColorSpace* pColorSpace =
+  RetainPtr<CPDF_ColorSpace> pColorSpace =
       pDocument->LoadColorSpace(pCSObj, pPageResources);
-  if (!pColorSpace)
-    return StartDIBBase();
-  int format = pColorSpace->GetFamily();
-  if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
-      format == PDFCS_DEVICEN) {
-    m_BlendType = BlendMode::kDarken;
+  if (pColorSpace) {
+    int format = pColorSpace->GetFamily();
+    if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
+        format == PDFCS_DEVICEN) {
+      m_BlendType = BlendMode::kDarken;
+    }
   }
-  pDocument->GetPageData()->ReleaseColorSpace(pCSObj);
   return StartDIBBase();
 }
 
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 24ecc85..226351d 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -111,7 +111,7 @@
 
 uint32_t GetValidatedOutputsCount(
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const CPDF_ColorSpace* pCS) {
+    const RetainPtr<CPDF_ColorSpace>& pCS) {
   uint32_t funcs_outputs = CountOutputsFromFunctions(funcs);
   return funcs_outputs ? std::max(funcs_outputs, pCS->CountComponents()) : 0;
 }
@@ -119,7 +119,7 @@
 void GetShadingSteps(float t_min,
                      float t_max,
                      const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                     const CPDF_ColorSpace* pCS,
+                     const RetainPtr<CPDF_ColorSpace>& pCS,
                      int alpha,
                      size_t results_count,
                      uint32_t* rgb_array) {
@@ -151,7 +151,7 @@
                       const CFX_Matrix& mtObject2Bitmap,
                       const CPDF_Dictionary* pDict,
                       const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                      const CPDF_ColorSpace* pCS,
+                      const RetainPtr<CPDF_ColorSpace>& pCS,
                       int alpha) {
   ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
 
@@ -222,7 +222,7 @@
                        const CFX_Matrix& mtObject2Bitmap,
                        const CPDF_Dictionary* pDict,
                        const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                       const CPDF_ColorSpace* pCS,
+                       const RetainPtr<CPDF_ColorSpace>& pCS,
                        int alpha) {
   ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
 
@@ -325,7 +325,7 @@
                      const CFX_Matrix& mtObject2Bitmap,
                      const CPDF_Dictionary* pDict,
                      const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-                     const CPDF_ColorSpace* pCS,
+                     const RetainPtr<CPDF_ColorSpace>& pCS,
                      int alpha) {
   ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
 
@@ -485,7 +485,7 @@
     const CFX_Matrix& mtObject2Bitmap,
     const CPDF_Stream* pShadingStream,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const CPDF_ColorSpace* pCS,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
     int alpha) {
   ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
 
@@ -526,7 +526,7 @@
     const CFX_Matrix& mtObject2Bitmap,
     const CPDF_Stream* pShadingStream,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const CPDF_ColorSpace* pCS,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
     int alpha) {
   ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
 
@@ -843,7 +843,7 @@
     const CFX_Matrix& mtObject2Bitmap,
     const CPDF_Stream* pShadingStream,
     const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
-    const CPDF_ColorSpace* pCS,
+    const RetainPtr<CPDF_ColorSpace>& pCS,
     bool bNoPathSmooth,
     int alpha) {
   ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
@@ -1481,7 +1481,7 @@
                                     ->GetStream()
                                     ->GetDict()
                                     ->GetDirectObjectFor("ColorSpace");
-    const CPDF_ColorSpace* pColorSpace =
+    RetainPtr<CPDF_ColorSpace> pColorSpace =
         pDocument->LoadColorSpace(pCSObj, pPageResources);
     if (pColorSpace) {
       int format = pColorSpace->GetFamily();
@@ -1489,7 +1489,6 @@
           format == PDFCS_DEVICEN) {
         blend_type = BlendMode::kDarken;
       }
-      pDocument->GetPageData()->ReleaseColorSpace(pCSObj);
     }
   }
   if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
@@ -2012,7 +2011,7 @@
                                     bool bAlphaMode) {
   const auto& funcs = pPattern->GetFuncs();
   const CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict();
-  const CPDF_ColorSpace* pColorSpace = pPattern->GetCS();
+  RetainPtr<CPDF_ColorSpace> pColorSpace = pPattern->GetCS();
   if (!pColorSpace)
     return;
 
@@ -2612,7 +2611,7 @@
       pGroupDict ? pGroupDict->GetDictFor("Group") : nullptr;
   if (pGroup)
     pCSObj = pGroup->GetDirectObjectFor(pdfium::transparency::kCS);
-  const CPDF_ColorSpace* pCS =
+  RetainPtr<CPDF_ColorSpace> pCS =
       m_pContext->GetDocument()->LoadColorSpace(pCSObj, nullptr);
   if (!pCS)
     return kDefaultColor;
@@ -2636,7 +2635,6 @@
   float G;
   float B;
   pCS->GetRGB(floats.data(), &R, &G, &B);
-  m_pContext->GetDocument()->GetPageData()->ReleaseColorSpace(pCSObj);
   return ArgbEncode(255, static_cast<int>(R * 255), static_cast<int>(G * 255),
                     static_cast<int>(B * 255));
 }
diff --git a/core/fxcodec/codec/ccodec_jpxmodule.cpp b/core/fxcodec/codec/ccodec_jpxmodule.cpp
index 431bd27..896754c 100644
--- a/core/fxcodec/codec/ccodec_jpxmodule.cpp
+++ b/core/fxcodec/codec/ccodec_jpxmodule.cpp
@@ -462,7 +462,7 @@
   img->comps[2].dy = img->comps[0].dy;
 }
 
-CJPX_Decoder::CJPX_Decoder(CPDF_ColorSpace* cs)
+CJPX_Decoder::CJPX_Decoder(const RetainPtr<CPDF_ColorSpace>& cs)
     : m_Image(nullptr),
       m_Codec(nullptr),
       m_DecodeData(nullptr),
@@ -656,7 +656,7 @@
 
 std::unique_ptr<CJPX_Decoder> CCodec_JpxModule::CreateDecoder(
     pdfium::span<const uint8_t> src_span,
-    CPDF_ColorSpace* cs) {
+    const RetainPtr<CPDF_ColorSpace>& cs) {
   auto decoder = pdfium::MakeUnique<CJPX_Decoder>(cs);
   if (!decoder->Init(src_span))
     return nullptr;
diff --git a/core/fxcodec/codec/ccodec_jpxmodule.h b/core/fxcodec/codec/ccodec_jpxmodule.h
index 2269ef3..fd4bd64 100644
--- a/core/fxcodec/codec/ccodec_jpxmodule.h
+++ b/core/fxcodec/codec/ccodec_jpxmodule.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "third_party/base/span.h"
 
 class CJPX_Decoder;
@@ -23,7 +24,7 @@
 
   std::unique_ptr<CJPX_Decoder> CreateDecoder(
       pdfium::span<const uint8_t> src_span,
-      CPDF_ColorSpace* cs);
+      const RetainPtr<CPDF_ColorSpace>& cs);
 
   void GetImageInfo(CJPX_Decoder* pDecoder,
                     uint32_t* width,
diff --git a/core/fxcodec/codec/cjpx_decoder.h b/core/fxcodec/codec/cjpx_decoder.h
index 3b6f547..95334b6 100644
--- a/core/fxcodec/codec/cjpx_decoder.h
+++ b/core/fxcodec/codec/cjpx_decoder.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "core/fxcodec/codec/codec_int.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/span.h"
 
@@ -24,7 +25,7 @@
 
 class CJPX_Decoder {
  public:
-  explicit CJPX_Decoder(CPDF_ColorSpace* cs);
+  explicit CJPX_Decoder(const RetainPtr<CPDF_ColorSpace>& cs);
   ~CJPX_Decoder();
 
   bool Init(pdfium::span<const uint8_t> src_data);
@@ -41,7 +42,7 @@
   std::unique_ptr<DecodeData> m_DecodeData;
   UnownedPtr<opj_stream_t> m_Stream;
   opj_dparameters_t m_Parameters;
-  UnownedPtr<const CPDF_ColorSpace> const m_ColorSpace;
+  RetainPtr<CPDF_ColorSpace> const m_ColorSpace;
 };
 
 #endif  // CORE_FXCODEC_CODEC_CJPX_DECODER_H_
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index b0a9cfa..d7a429b 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -13,6 +13,7 @@
 
 #include "constants/page_object.h"
 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_formobject.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index 24b3b67..9d2702b 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -491,6 +491,7 @@
     "pdf_jpx_fuzzer.cc",
   ]
   deps = [
+    "../../core/fpdfapi/page",
     "../../core/fxcodec",
     "../../core/fxcrt",
     "../../core/fxge",
diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc
index 51601f6..8a9c63f 100644
--- a/testing/fuzzers/pdf_jpx_fuzzer.cc
+++ b/testing/fuzzers/pdf_jpx_fuzzer.cc
@@ -6,6 +6,7 @@
 #include <memory>
 #include <vector>
 
+#include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcodec/codec/ccodec_jpxmodule.h"
 #include "core/fxcodec/codec/cjpx_decoder.h"
 #include "core/fxcrt/fx_safe_types.h"