Maintain a stack of CPDF_ContentMark while parsing a stream.

This avoids copying the CPDF_ContentMark every time a mark is closed.
Another benefit is that the same CPDF_ContentMarkItem vector will be
shared by page objects before and after a nested mark.

Bug: pdfium:1037
Change-Id: I6197f0b9a4693ef84da9269f86a2629aa50d8685
Reviewed-on: https://pdfium-review.googlesource.com/37190
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index c698c52..640b7f7 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -258,7 +258,6 @@
       m_ParamStartPos(0),
       m_ParamCount(0),
       m_pCurStates(pdfium::MakeUnique<CPDF_AllStates>()),
-      m_pCurContentMark(pdfium::MakeUnique<CPDF_ContentMark>()),
       m_DefFontSize(0),
       m_PathStartX(0.0f),
       m_PathStartY(0.0f),
@@ -284,6 +283,9 @@
   for (size_t i = 0; i < FX_ArraySize(m_Type3Data); ++i) {
     m_Type3Data[i] = 0.0;
   }
+
+  // Add the sentinel.
+  m_ContentMarksStack.push(pdfium::MakeUnique<CPDF_ContentMark>());
 }
 
 CPDF_StreamContentParser::~CPDF_StreamContentParser() {
@@ -435,7 +437,7 @@
                                                 bool bGraph) {
   pObj->m_GeneralState = m_pCurStates->m_GeneralState;
   pObj->m_ClipPath = m_pCurStates->m_ClipPath;
-  pObj->m_ContentMark = *m_pCurContentMark;
+  pObj->m_ContentMark = *m_ContentMarksStack.top();
   if (bColor) {
     pObj->m_ColorState = m_pCurStates->m_ColorState;
   }
@@ -608,8 +610,10 @@
     bDirect = false;
   }
   if (const CPDF_Dictionary* pDict = pProperty->AsDictionary()) {
-    m_pCurContentMark = m_pCurContentMark->Clone();
-    m_pCurContentMark->AddMark(std::move(tag), pDict, bDirect);
+    std::unique_ptr<CPDF_ContentMark> new_marks =
+        m_ContentMarksStack.top()->Clone();
+    new_marks->AddMark(std::move(tag), pDict, bDirect);
+    m_ContentMarksStack.push(std::move(new_marks));
   }
 }
 
@@ -674,8 +678,10 @@
 }
 
 void CPDF_StreamContentParser::Handle_BeginMarkedContent() {
-  m_pCurContentMark = m_pCurContentMark->Clone();
-  m_pCurContentMark->AddMark(GetString(0), nullptr, false);
+  std::unique_ptr<CPDF_ContentMark> new_marks =
+      m_ContentMarksStack.top()->Clone();
+  new_marks->AddMark(GetString(0), nullptr, false);
+  m_ContentMarksStack.push(std::move(new_marks));
 }
 
 void CPDF_StreamContentParser::Handle_BeginText() {
@@ -868,8 +874,10 @@
 void CPDF_StreamContentParser::Handle_EndImage() {}
 
 void CPDF_StreamContentParser::Handle_EndMarkedContent() {
-  m_pCurContentMark = m_pCurContentMark->Clone();
-  m_pCurContentMark->DeleteLastMark();
+  // First element is a sentinel, so do not pop it, ever. This may come up if
+  // the EMCs are mismatched with the BMC/BDCs.
+  if (m_ContentMarksStack.size() > 1)
+    m_ContentMarksStack.pop();
 }
 
 void CPDF_StreamContentParser::Handle_EndText() {
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h
index ae0a7f3..238999b 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.h
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.h
@@ -10,6 +10,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <stack>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_contentmark.h"
@@ -219,7 +220,7 @@
   uint32_t m_ParamCount;
   UnownedPtr<CPDF_StreamParser> m_pSyntax;
   std::unique_ptr<CPDF_AllStates> m_pCurStates;
-  std::unique_ptr<CPDF_ContentMark> m_pCurContentMark;
+  std::stack<std::unique_ptr<CPDF_ContentMark>> m_ContentMarksStack;
   std::vector<std::unique_ptr<CPDF_TextObject>> m_ClipTextList;
   UnownedPtr<CPDF_TextObject> m_pLastTextObject;
   float m_DefFontSize;