Implement read Session.

Helper for validate read problems locally,

Change-Id: I2bfbbaab8a67c0fd0cee3dadcd0f9cad5953101c
Reviewed-on: https://pdfium-review.googlesource.com/9552
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Art Snake <art-snake@yandex-team.ru>
diff --git a/core/fpdfapi/parser/cpdf_read_validator.cpp b/core/fpdfapi/parser/cpdf_read_validator.cpp
index 148ecfd..be20f18 100644
--- a/core/fpdfapi/parser/cpdf_read_validator.cpp
+++ b/core/fpdfapi/parser/cpdf_read_validator.cpp
@@ -26,6 +26,19 @@
 
 }  // namespace
 
+CPDF_ReadValidator::Session::Session(CPDF_ReadValidator* validator)
+    : validator_(validator) {
+  ASSERT(validator_);
+  saved_read_error_ = validator_->read_error_;
+  saved_has_unavailable_data_ = validator_->has_unavailable_data_;
+  validator_->ResetErrors();
+}
+
+CPDF_ReadValidator::Session::~Session() {
+  validator_->read_error_ |= saved_read_error_;
+  validator_->has_unavailable_data_ |= saved_has_unavailable_data_;
+}
+
 CPDF_ReadValidator::CPDF_ReadValidator(
     const CFX_RetainPtr<IFX_SeekableReadStream>& file_read,
     CPDF_DataAvail::FileAvail* file_avail)
diff --git a/core/fpdfapi/parser/cpdf_read_validator.h b/core/fpdfapi/parser/cpdf_read_validator.h
index da8acfe..9cc22c1 100644
--- a/core/fpdfapi/parser/cpdf_read_validator.h
+++ b/core/fpdfapi/parser/cpdf_read_validator.h
@@ -12,6 +12,17 @@
   template <typename T, typename... Args>
   friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args);
 
+  class Session {
+   public:
+    explicit Session(CPDF_ReadValidator* validator);
+    ~Session();
+
+   private:
+    CFX_UnownedPtr<CPDF_ReadValidator> validator_;
+    bool saved_read_error_;
+    bool saved_has_unavailable_data_;
+  };
+
   void SetDownloadHints(CPDF_DataAvail::DownloadHints* hints) {
     hints_ = hints;
   }
diff --git a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
index f0e47f5..cb6c07e 100644
--- a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp
@@ -178,3 +178,83 @@
   EXPECT_FALSE(validator->read_error());
   EXPECT_FALSE(validator->has_unavailable_data());
 }
+
+TEST(CPDF_ReadValidatorTest, Session) {
+  std::vector<uint8_t> test_data(kTestDataSize);
+
+  auto file = pdfium::MakeRetain<InvalidReader>();
+  MockFileAvail file_avail;
+  MockDownloadHints hints;
+  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  validator->SetDownloadHints(&hints);
+
+  const CPDF_ReadValidator::Session read_session(validator.Get());
+  ASSERT_FALSE(validator->has_read_problems());
+
+  // Data is unavailable
+  validator->ReadBlock(test_data.data(), 0, 100);
+
+  EXPECT_TRUE(validator->has_read_problems());
+  EXPECT_TRUE(validator->has_unavailable_data());
+  EXPECT_FALSE(validator->read_error());
+
+  {
+    const CPDF_ReadValidator::Session read_subsession(validator.Get());
+    // The read problems should be hidden.
+    EXPECT_FALSE(validator->has_read_problems());
+
+    file_avail.SetAvailableRange(0, 100);
+    // Read fail.
+    validator->ReadBlock(test_data.data(), 0, 100);
+    EXPECT_TRUE(validator->has_read_problems());
+    EXPECT_TRUE(validator->has_unavailable_data());
+    EXPECT_TRUE(validator->read_error());
+  }
+
+  // The problems should be merged
+  EXPECT_TRUE(validator->has_read_problems());
+  EXPECT_TRUE(validator->has_unavailable_data());
+  EXPECT_TRUE(validator->read_error());
+}
+
+TEST(CPDF_ReadValidatorTest, SessionReset) {
+  std::vector<uint8_t> test_data(kTestDataSize);
+
+  auto file = pdfium::MakeRetain<InvalidReader>();
+  MockFileAvail file_avail;
+  MockDownloadHints hints;
+  auto validator = pdfium::MakeRetain<CPDF_ReadValidator>(file, &file_avail);
+  validator->SetDownloadHints(&hints);
+
+  const CPDF_ReadValidator::Session read_session(validator.Get());
+  ASSERT_FALSE(validator->has_read_problems());
+
+  // Data is unavailable
+  validator->ReadBlock(test_data.data(), 0, 100);
+
+  EXPECT_TRUE(validator->has_read_problems());
+  EXPECT_TRUE(validator->has_unavailable_data());
+  EXPECT_FALSE(validator->read_error());
+
+  {
+    const CPDF_ReadValidator::Session read_subsession(validator.Get());
+    // The read problems should be hidden.
+    EXPECT_FALSE(validator->has_read_problems());
+
+    file_avail.SetAvailableRange(0, 100);
+    // Read fail.
+    validator->ReadBlock(test_data.data(), 0, 100);
+    EXPECT_TRUE(validator->has_read_problems());
+    EXPECT_TRUE(validator->has_unavailable_data());
+    EXPECT_TRUE(validator->read_error());
+
+    // Reset session.
+    validator->ResetErrors();
+    EXPECT_FALSE(validator->has_read_problems());
+  }
+
+  // The problems should be restored.
+  EXPECT_TRUE(validator->has_read_problems());
+  EXPECT_TRUE(validator->has_unavailable_data());
+  EXPECT_FALSE(validator->read_error());
+}