Add embedder tests for progressive render public APIs

The CL adds the FPDFProgressiveRenderEmbedderTest class which
contains the following:
1. Methods which calls into the progressive render public APIs.
2. Member variables to keep the bitmap and render flags alive
while the rendering is going on.
3. Four embedder tests for different scnearios around the usage of
the render APIs.

Bug: pdfium:1383
Change-Id: I713ccde9ffcca826b2de37f10b9ed1c6dfe54db9
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/59730
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/render/BUILD.gn b/core/fpdfapi/render/BUILD.gn
index bba7cb9..8378de6 100644
--- a/core/fpdfapi/render/BUILD.gn
+++ b/core/fpdfapi/render/BUILD.gn
@@ -74,6 +74,7 @@
 
 pdfium_embeddertest_source_set("embeddertests") {
   sources = [
+    "fpdf_progressive_render_embeddertest.cpp",
     "fpdf_render_pattern_embeddertest.cpp",
   ]
   pdfium_root_dir = "../../../"
diff --git a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
new file mode 100644
index 0000000..3add124
--- /dev/null
+++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
@@ -0,0 +1,221 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "build/build_config.h"
+#include "core/fxcrt/fx_system.h"
+#include "public/fpdf_progressive.h"
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFProgressiveRenderEmbedderTest : public EmbedderTest {
+ public:
+  // StartRenderPageWithFlags() with no flags.
+  // The call returns true if the rendering is complete.
+  bool StartRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause);
+
+  // Start rendering of |page| into a bitmap with the ability to pause the
+  // rendering with the specified rendering |flags|.
+  // The call returns true if the rendering is complete.
+  //
+  // See public/fpdfview.h for a list of page rendering flags.
+  bool StartRenderPageWithFlags(FPDF_PAGE page, IFSDK_PAUSE* pause, int flags);
+
+  // Continue rendering of |page| into the bitmap created in
+  // StartRenderPageWithFlags().
+  // The call returns true if the rendering is complete.
+  bool ContinueRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause);
+
+  // Simplified form of FinishRenderPageWithForms() with no form handle.
+  ScopedFPDFBitmap FinishRenderPage(FPDF_PAGE page);
+
+  // Finish rendering of |page| into the bitmap created in
+  // StartRenderPageWithFlags(). This also renders the forms associated with
+  // the page. The form handle associated with |page| should be passed in via
+  // |handle|. If |handle| is nullptr, then forms on the page will not be
+  // rendered.
+  // This returns the bitmap generated by the progressive render calls.
+  ScopedFPDFBitmap FinishRenderPageWithForms(FPDF_PAGE page,
+                                             FPDF_FORMHANDLE handle);
+
+ private:
+  // Keeps the bitmap used for progressive rendering alive until
+  // FPDF_RenderPage_Close() is called after which the bitmap is returned
+  // to the caller.
+  ScopedFPDFBitmap progressive_render_bitmap_;
+  int progressive_render_flags_ = 0;
+};
+
+bool FPDFProgressiveRenderEmbedderTest::StartRenderPage(FPDF_PAGE page,
+                                                        IFSDK_PAUSE* pause) {
+  return StartRenderPageWithFlags(page, pause, 0);
+}
+
+bool FPDFProgressiveRenderEmbedderTest::StartRenderPageWithFlags(
+    FPDF_PAGE page,
+    IFSDK_PAUSE* pause,
+    int flags) {
+  int width = static_cast<int>(FPDF_GetPageWidth(page));
+  int height = static_cast<int>(FPDF_GetPageHeight(page));
+  progressive_render_flags_ = flags;
+  int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
+  progressive_render_bitmap_ =
+      ScopedFPDFBitmap(FPDFBitmap_Create(width, height, alpha));
+  FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
+  FPDFBitmap_FillRect(progressive_render_bitmap_.get(), 0, 0, width, height,
+                      fill_color);
+  int rv = FPDF_RenderPageBitmap_Start(progressive_render_bitmap_.get(), page,
+                                       0, 0, width, height, 0,
+                                       progressive_render_flags_, pause);
+  return rv != FPDF_RENDER_TOBECONTINUED;
+}
+
+bool FPDFProgressiveRenderEmbedderTest::ContinueRenderPage(FPDF_PAGE page,
+                                                           IFSDK_PAUSE* pause) {
+  ASSERT(progressive_render_bitmap_);
+
+  int rv = FPDF_RenderPage_Continue(page, pause);
+  return rv != FPDF_RENDER_TOBECONTINUED;
+}
+
+ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPage(
+    FPDF_PAGE page) {
+  return FinishRenderPageWithForms(page, /*handle=*/nullptr);
+}
+
+ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPageWithForms(
+    FPDF_PAGE page,
+    FPDF_FORMHANDLE handle) {
+  ASSERT(progressive_render_bitmap_);
+
+  int width = static_cast<int>(FPDF_GetPageWidth(page));
+  int height = static_cast<int>(FPDF_GetPageHeight(page));
+  FPDF_FFLDraw(handle, progressive_render_bitmap_.get(), page, 0, 0, width,
+               height, 0, progressive_render_flags_);
+  FPDF_RenderPage_Close(page);
+  return std::move(progressive_render_bitmap_);
+}
+
+class FakePause : public IFSDK_PAUSE {
+ public:
+  explicit FakePause(bool should_pause) : should_pause_(should_pause) {
+    IFSDK_PAUSE::version = 1;
+    IFSDK_PAUSE::user = nullptr;
+    IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;
+  }
+  ~FakePause() = default;
+
+  static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) {
+    return static_cast<FakePause*>(param)->should_pause_;
+  }
+
+ private:
+  const bool should_pause_ = false;
+};
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithoutPause) {
+#if defined(OS_WIN)
+  static constexpr char kMd5BaseContent[] = "649d6792ea50faf98c013c2d81710595";
+#elif defined(OS_MACOSX)
+  static constexpr char kMd5BaseContent[] = "5f933aac2a74434be1b4d0bdb5334f0b";
+#else
+  static constexpr char kMd5BaseContent[] = "a24edc7740f1d6f76899652dcf825dea";
+#endif
+
+  // Test rendering of page content using progressive render APIs
+  // without pausing the rendering.
+  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(false);
+  EXPECT_TRUE(StartRenderPage(page, &pause));
+  ScopedFPDFBitmap bitmap = FinishRenderPage(page);
+  CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithPause) {
+#if defined(OS_WIN)
+  static constexpr char kMd5BaseContent[] = "649d6792ea50faf98c013c2d81710595";
+#elif defined(OS_MACOSX)
+  static constexpr char kMd5BaseContent[] = "5f933aac2a74434be1b4d0bdb5334f0b";
+#else
+  static constexpr char kMd5BaseContent[] = "a24edc7740f1d6f76899652dcf825dea";
+#endif
+
+  // Test rendering of page content using progressive render APIs
+  // with pause in rendering.
+  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(true);
+  bool render_done = StartRenderPage(page, &pause);
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
+  }
+  ScopedFPDFBitmap bitmap = FinishRenderPage(page);
+  CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderAnnotWithPause) {
+#if defined(OS_WIN)
+  static constexpr char kMd5ContentWithAnnot[] =
+      "6aa001a77ec05d0f1b0d1d22e28744d4";
+#elif defined(OS_MACOSX)
+  static constexpr char kMd5ContentWithAnnot[] =
+      "c35408717759562d1f8bf33d317483d2";
+#else
+  static constexpr char kMd5ContentWithAnnot[] =
+      "b42cef463483e668eaf4055a65e4f1f5";
+#endif
+
+  // Test rendering of the page with annotations using progressive render APIs
+  // with pause in rendering.
+  EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(true);
+  bool render_done = StartRenderPageWithFlags(page, &pause, FPDF_ANNOT);
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
+  }
+  ScopedFPDFBitmap bitmap = FinishRenderPage(page);
+  CompareBitmap(bitmap.get(), 595, 842, kMd5ContentWithAnnot);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormsWithPause) {
+#if defined(OS_WIN)
+  static constexpr char kMd5ContentWithForms[] =
+      "d3204faa62b607f0bd3893c9c22cabcb";
+#elif defined(OS_MACOSX)
+  static constexpr char kMd5ContentWithForms[] =
+      "5f11dbe575fe197a37c3fb422559f8ff";
+#else
+  static constexpr char kMd5ContentWithForms[] =
+      "b890950d4b9bc163b1a96797f3004b53";
+#endif
+
+  // Test rendering of the page with forms using progressive render APIs
+  // with pause in rendering.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  FakePause pause(true);
+  bool render_done = StartRenderPage(page, &pause);
+  EXPECT_FALSE(render_done);
+
+  while (!render_done) {
+    render_done = ContinueRenderPage(page, &pause);
+  }
+  ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle_);
+  CompareBitmap(bitmap.get(), 300, 300, kMd5ContentWithForms);
+  UnloadPage(page);
+}