Disable JavaScript entirely if no JSPlatform passed by embedder.

Allows run-time selection of whether to permit JS inside PDF. Previously,
this was a link-time decision only. This requires a little more caution
before we decide that we have the CJS_Runtime, and not the CJS_RuntimeStub
in a few casts.

Adds a kDisableJavaScript option to the form fill embeddertests.
Adds a --disable-javascript flag to the pdfium_test executable.
Also adds a --disable-xfa flag while we're at it.

Change-Id: I8d8ac95f6474459cadba9a60572fbb342e984646
Reviewed-on: https://pdfium-review.googlesource.com/31090
Reviewed-by: dsinclair <dsinclair@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
index df7b5b6..4937b3d 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -51,7 +51,9 @@
   const char md5[] = "a5dde3c6c37b8716b9b369a03752a728";
 #endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
   {
-    ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678", true));
+    ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678",
+                                        LinearizeOption::kMustLinearize,
+                                        JavaScriptOption::kEnableJavaScript));
     FPDF_PAGE page = LoadPage(0);
     ASSERT_TRUE(page);
     FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
index b3375d4..272e270 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
@@ -203,8 +203,6 @@
 }
 
 IJS_Runtime* CPDFSDK_FormFillEnvironment::GetIJSRuntime() {
-  if (!IsJSPlatformPresent())
-    return nullptr;
   if (!m_pIJSRuntime)
     m_pIJSRuntime = IJS_Runtime::Create(this);
   return m_pIJSRuntime.get();
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index 09e0c20..cedb96c 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -380,6 +380,36 @@
 }
 
 #ifdef PDF_ENABLE_V8
+TEST_F(FPDFFormFillEmbeddertest, DisableJavaScript) {
+  // Test that timers and intervals can't fire without JS.
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  EXPECT_TRUE(OpenDocumentWithoutJavaScript("bug_551248.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+  DoOpenActions();
+
+  const auto& alerts = delegate.GetAlerts();
+  EXPECT_EQ(0U, alerts.size());
+
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  delegate.AdvanceTime(1000);
+  EXPECT_EQ(0U, alerts.size());  // nothing fired.
+  UnloadPage(page);
+}
+
 TEST_F(FPDFFormFillEmbeddertest, BUG_551248) {
   // Test that timers fire once and intervals fire repeatedly.
   EmbedderTestTimerHandlingDelegate delegate;
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
index c85c1bd..7d1b28b 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
@@ -81,11 +81,11 @@
 
 bool CPDFXFA_Context::LoadXFADoc() {
   m_nLoadStatus = FXFA_LOADSTATUS_LOADING;
+  m_XFAPageList.clear();
+
   if (!m_pPDFDoc)
     return false;
 
-  m_XFAPageList.clear();
-
   CXFA_FFApp* pApp = GetXFAApp();
   if (!pApp)
     return false;
@@ -96,8 +96,13 @@
     return false;
   }
 
-  m_pXFADoc->GetXFADoc()->InitScriptContext(GetCJSRuntime());
+  CJS_Runtime* actual_runtime = GetCJSRuntime();  // Null if a stub.
+  if (!actual_runtime) {
+    SetLastError(FPDF_ERR_XFALOAD);
+    return false;
+  }
 
+  m_pXFADoc->GetXFADoc()->InitScriptContext(actual_runtime);
   if (m_pXFADoc->GetFormType() == FormType::kXFAFull)
     m_FormType = FormType::kXFAFull;
   else
@@ -113,7 +118,6 @@
   m_pXFADocView->DoLayout();
   m_pXFADocView->StopLayout();
   m_nLoadStatus = FXFA_LOADSTATUS_LOADED;
-
   return true;
 }
 
@@ -196,8 +200,7 @@
   if (!m_pFormFillEnv)
     return nullptr;
 
-  // XFA requires V8, if we have V8 then we have a CJS_Runtime and not the stub.
-  return static_cast<CJS_Runtime*>(m_pFormFillEnv->GetIJSRuntime());
+  return m_pFormFillEnv->GetIJSRuntime()->AsCJSRuntime();
 }
 
 WideString CPDFXFA_Context::GetAppTitle() const {
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index 248116b..9cfcad3 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -132,8 +132,7 @@
 CJS_Document::~CJS_Document() = default;
 
 void CJS_Document::InitInstance(IJS_Runtime* pIRuntime) {
-  CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
-  SetFormFillEnv(pRuntime->GetFormFillEnv());
+  SetFormFillEnv(pIRuntime->GetFormFillEnv());
 }
 
 // The total number of fields in document.
diff --git a/fxjs/cjs_global.cpp b/fxjs/cjs_global.cpp
index c8deadf..282b262 100644
--- a/fxjs/cjs_global.cpp
+++ b/fxjs/cjs_global.cpp
@@ -214,8 +214,7 @@
 }
 
 void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) {
-  CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
-  Initial(pRuntime->GetFormFillEnv());
+  Initial(pIRuntime->GetFormFillEnv());
 }
 
 void CJS_Global::Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv) {
diff --git a/fxjs/cjs_runtime.cpp b/fxjs/cjs_runtime.cpp
index b9b9c03..3a607f1 100644
--- a/fxjs/cjs_runtime.cpp
+++ b/fxjs/cjs_runtime.cpp
@@ -143,6 +143,10 @@
   CJS_Annot::DefineJSObjects(this);
 }
 
+CJS_Runtime* CJS_Runtime::AsCJSRuntime() {
+  return this;
+}
+
 IJS_EventContext* CJS_Runtime::NewEventContext() {
   m_EventContextArray.push_back(pdfium::MakeUnique<CJS_EventContext>(this));
   return m_EventContextArray.back().get();
diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h
index 0aab848..c5e69fb 100644
--- a/fxjs/cjs_runtime.h
+++ b/fxjs/cjs_runtime.h
@@ -33,6 +33,7 @@
   ~CJS_Runtime() override;
 
   // IJS_Runtime
+  CJS_Runtime* AsCJSRuntime() override;
   IJS_EventContext* NewEventContext() override;
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
diff --git a/fxjs/cjs_runtimestub.cpp b/fxjs/cjs_runtimestub.cpp
index 98c7679..c1e680e 100644
--- a/fxjs/cjs_runtimestub.cpp
+++ b/fxjs/cjs_runtimestub.cpp
@@ -14,6 +14,10 @@
 
 CJS_RuntimeStub::~CJS_RuntimeStub() = default;
 
+CJS_Runtime* CJS_RuntimeStub::AsCJSRuntime() {
+  return nullptr;
+}
+
 IJS_EventContext* CJS_RuntimeStub::NewEventContext() {
   if (!m_pContext)
     m_pContext = pdfium::MakeUnique<CJS_EventContextStub>();
diff --git a/fxjs/cjs_runtimestub.h b/fxjs/cjs_runtimestub.h
index ecf3b4e..a9e85fd 100644
--- a/fxjs/cjs_runtimestub.h
+++ b/fxjs/cjs_runtimestub.h
@@ -21,6 +21,7 @@
   explicit CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv);
   ~CJS_RuntimeStub() override;
 
+  CJS_Runtime* AsCJSRuntime() override;
   IJS_EventContext* NewEventContext() override;
   void ReleaseEventContext(IJS_EventContext* pContext) override;
   CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override;
diff --git a/fxjs/ijs_runtime.cpp b/fxjs/ijs_runtime.cpp
index 03538cd..79238bf 100644
--- a/fxjs/ijs_runtime.cpp
+++ b/fxjs/ijs_runtime.cpp
@@ -31,8 +31,10 @@
 std::unique_ptr<IJS_Runtime> IJS_Runtime::Create(
     CPDFSDK_FormFillEnvironment* pFormFillEnv) {
 #ifdef PDF_ENABLE_V8
-  return pdfium::MakeUnique<CJS_Runtime>(pFormFillEnv);
-#else
-  return pdfium::MakeUnique<CJS_RuntimeStub>(pFormFillEnv);
+  if (pFormFillEnv->IsJSPlatformPresent())
+    return pdfium::MakeUnique<CJS_Runtime>(pFormFillEnv);
 #endif
+  return pdfium::MakeUnique<CJS_RuntimeStub>(pFormFillEnv);
 }
+
+IJS_Runtime::~IJS_Runtime() = default;
diff --git a/fxjs/ijs_runtime.h b/fxjs/ijs_runtime.h
index b97c65e..cde31c6 100644
--- a/fxjs/ijs_runtime.h
+++ b/fxjs/ijs_runtime.h
@@ -16,18 +16,22 @@
 #include "fxjs/fxjse.h"
 #endif  // PDF_ENABLE_XFA
 
+class CJS_Runtime;
 class CPDFSDK_FormFillEnvironment;
 class IJS_EventContext;
 
-// Owns the FJXS objects needed to actually execute JS.
+// Owns the FJXS objects needed to actually execute JS, if possible. This
+// virtual interface is backed by either an actual JS runtime, or a stub,
+// when JS is not present.
 class IJS_Runtime {
  public:
   static void Initialize(unsigned int slot, void* isolate);
   static void Destroy();
   static std::unique_ptr<IJS_Runtime> Create(
       CPDFSDK_FormFillEnvironment* pFormFillEnv);
-  virtual ~IJS_Runtime() {}
+  virtual ~IJS_Runtime();
 
+  virtual CJS_Runtime* AsCJSRuntime() = 0;
   virtual IJS_EventContext* NewEventContext() = 0;
   virtual void ReleaseEventContext(IJS_EventContext* pContext) = 0;
   virtual CPDFSDK_FormFillEnvironment* GetFormFillEnv() const = 0;
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index 3dcd831..292673f 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -88,40 +88,32 @@
 namespace {
 
 struct Options {
-  Options()
-      : show_config(false),
-        show_metadata(false),
-        send_events(false),
-        render_oneshot(false),
-        save_attachments(false),
-        save_images(false),
-#ifdef ENABLE_CALLGRIND
-        callgrind_delimiters(false),
-#endif  // ENABLE_CALLGRIND
-        pages(false),
-        md5(false),
-        output_format(OUTPUT_NONE) {
-  }
+  Options() = default;
 
-  bool show_config;
-  bool show_metadata;
-  bool send_events;
-  bool render_oneshot;
-  bool save_attachments;
-  bool save_images;
+  bool show_config = false;
+  bool show_metadata = false;
+  bool send_events = false;
+  bool render_oneshot = false;
+  bool save_attachments = false;
+  bool save_images = false;
+#ifdef PDF_ENABLE_V8
+  bool disable_javascript = false;
+#ifdef PDF_ENABLE_XFA
+  bool disable_xfa = false;
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
+  bool pages = false;
+  bool md5 = false;
 #ifdef ENABLE_CALLGRIND
-  bool callgrind_delimiters;
+  bool callgrind_delimiters = false;
 #endif  // ENABLE_CALLGRIND
-  bool pages;
-  bool md5;
-  OutputFormat output_format;
+  OutputFormat output_format = OUTPUT_NONE;
   std::string scale_factor_as_string;
   std::string exe_path;
   std::string bin_directory;
   std::string font_directory;
-  // 0-based page numbers to be rendered.
-  int first_page;
-  int last_page;
+  int first_page = 0;  // First 0-based page number to renderer.
+  int last_page = 0;   // Last 0-based page number to renderer.
 };
 
 Optional<std::string> ExpandDirectoryPath(const std::string& path) {
@@ -286,6 +278,14 @@
       options->save_attachments = true;
     } else if (cur_arg == "--save-images") {
       options->save_images = true;
+#if PDF_ENABLE_V8
+    } else if (cur_arg == "--disable-javascript") {
+      options->disable_javascript = true;
+#ifdef PDF_ENABLE_XFA
+    } else if (cur_arg == "--disable-xfa") {
+      options->disable_xfa = true;
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 #ifdef ENABLE_CALLGRIND
     } else if (cur_arg == "--callgrind-delim") {
       options->callgrind_delimiters = true;
@@ -695,17 +695,23 @@
   form_callbacks.version = 1;
 #endif  // PDF_ENABLE_XFA
   form_callbacks.FFI_GetPage = GetPageForIndex;
-  form_callbacks.m_pJsPlatform = &platform_callbacks;
+
+#ifdef PDF_ENABLE_V8
+  if (!options.disable_javascript)
+    form_callbacks.m_pJsPlatform = &platform_callbacks;
+#endif  // PDF_ENABLE_V8
 
   std::unique_ptr<void, FPDFFormHandleDeleter> form(
       FPDFDOC_InitFormFillEnvironment(doc.get(), &form_callbacks));
   form_callbacks.form_handle = form.get();
 
 #ifdef PDF_ENABLE_XFA
-  int doc_type = FPDF_GetFormType(doc.get());
-  if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
-    if (!FPDF_LoadXFA(doc.get()))
-      fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
+  if (!options.disable_xfa && !options.disable_javascript) {
+    int doc_type = FPDF_GetFormType(doc.get());
+    if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
+      if (!FPDF_LoadXFA(doc.get()))
+        fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
+    }
   }
 #endif  // PDF_ENABLE_XFA
 
@@ -789,6 +795,12 @@
     "<pdf-name>.attachment.<attachment-name>\n"
     "  --save-images       - write embedded images "
     "<pdf-name>.<page-number>.<object-number>.png\n"
+#ifdef PDF_ENABLE_V8
+    "  --disable-javascript- do not execute JS in PDF files"
+#ifdef PDF_ENABLE_XFA
+    "  --disable-xfa       - do not process XFA forms"
+#endif  // PDF_ENABLE_XFA
+#endif  // PDF_ENABLE_V8
 #ifdef ENABLE_CALLGRIND
     "  --callgrind-delim   - delimit interesting section when using callgrind\n"
 #endif  // ENABLE_CALLGRIND
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index c39539b..fd53b52 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -100,29 +100,44 @@
   if (!document_)
     return false;
 
-  form_handle_ = SetupFormFillEnvironment(document_);
+  form_handle_ =
+      SetupFormFillEnvironment(document_, JavaScriptOption::kEnableJavaScript);
   return true;
 }
 
 bool EmbedderTest::OpenDocument(const std::string& filename) {
-  return OpenDocumentWithOptions(filename, nullptr, false);
+  return OpenDocumentWithOptions(filename, nullptr,
+                                 LinearizeOption::kDefaultLinearize,
+                                 JavaScriptOption::kEnableJavaScript);
 }
 
 bool EmbedderTest::OpenDocumentLinearized(const std::string& filename) {
-  return OpenDocumentWithOptions(filename, nullptr, true);
+  return OpenDocumentWithOptions(filename, nullptr,
+                                 LinearizeOption::kMustLinearize,
+                                 JavaScriptOption::kEnableJavaScript);
 }
 
 bool EmbedderTest::OpenDocumentWithPassword(const std::string& filename,
                                             const char* password) {
-  return OpenDocumentWithOptions(filename, password, false);
+  return OpenDocumentWithOptions(filename, password,
+                                 LinearizeOption::kDefaultLinearize,
+                                 JavaScriptOption::kEnableJavaScript);
+}
+
+bool EmbedderTest::OpenDocumentWithoutJavaScript(const std::string& filename) {
+  return OpenDocumentWithOptions(filename, nullptr,
+                                 LinearizeOption::kDefaultLinearize,
+                                 JavaScriptOption::kDisableJavaScript);
 }
 
 bool EmbedderTest::OpenDocumentWithOptions(const std::string& filename,
                                            const char* password,
-                                           bool must_linearize) {
+                                           LinearizeOption linearize_option,
+                                           JavaScriptOption javascript_option) {
   std::string file_path;
   if (!PathService::GetTestFilePath(filename, &file_path))
     return false;
+
   file_contents_ = GetFileContents(file_path.c_str(), &file_length_);
   if (!file_contents_)
     return false;
@@ -136,12 +151,14 @@
   file_access_.m_Param = loader_;
 
   fake_file_access_ = pdfium::MakeUnique<FakeFileAccess>(&file_access_);
-  return OpenDocumentHelper(password, must_linearize, fake_file_access_.get(),
-                            &document_, &avail_, &form_handle_);
+  return OpenDocumentHelper(password, linearize_option, javascript_option,
+                            fake_file_access_.get(), &document_, &avail_,
+                            &form_handle_);
 }
 
 bool EmbedderTest::OpenDocumentHelper(const char* password,
-                                      bool must_linearize,
+                                      LinearizeOption linearize_option,
+                                      JavaScriptOption javascript_option,
                                       FakeFileAccess* network_simulator,
                                       FPDF_DOCUMENT* document,
                                       FPDF_AVAIL* avail,
@@ -186,7 +203,7 @@
         return false;
     }
   } else {
-    if (must_linearize)
+    if (linearize_option == LinearizeOption::kMustLinearize)
       return false;
     network_simulator->SetWholeFileAvailable();
     *document =
@@ -194,17 +211,21 @@
     if (!*document)
       return false;
   }
-  *form_handle = SetupFormFillEnvironment(*document);
+  *form_handle = SetupFormFillEnvironment(*document, javascript_option);
+
 #ifdef PDF_ENABLE_XFA
   int doc_type = FPDF_GetFormType(*document);
   if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND)
     FPDF_LoadXFA(*document);
 #endif  // PDF_ENABLE_XFA
+
   (void)FPDF_GetDocPermissions(*document);
   return true;
 }
 
-FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(FPDF_DOCUMENT doc) {
+FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(
+    FPDF_DOCUMENT doc,
+    JavaScriptOption javascript_option) {
   IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
   memset(platform, '\0', sizeof(IPDF_JSPLATFORM));
   platform->version = 2;
@@ -221,7 +242,9 @@
   formfillinfo->FFI_SetTimer = SetTimerTrampoline;
   formfillinfo->FFI_KillTimer = KillTimerTrampoline;
   formfillinfo->FFI_GetPage = GetPageTrampoline;
-  formfillinfo->m_pJsPlatform = platform;
+  if (javascript_option == JavaScriptOption::kEnableJavaScript)
+    formfillinfo->m_pJsPlatform = platform;
+
   FPDF_FORMHANDLE form_handle =
       FPDFDOC_InitFormFillEnvironment(doc, formfillinfo);
   FPDF_SetFormFieldHighlightColor(form_handle, FPDF_FORMFIELD_UNKNOWN,
@@ -337,9 +360,10 @@
   saved_fake_file_access_ =
       pdfium::MakeUnique<FakeFileAccess>(&saved_file_access_);
 
-  EXPECT_TRUE(OpenDocumentHelper(password, false, saved_fake_file_access_.get(),
-                                 &saved_document_, &saved_avail_,
-                                 &saved_form_handle_));
+  EXPECT_TRUE(OpenDocumentHelper(
+      password, LinearizeOption::kDefaultLinearize,
+      JavaScriptOption::kEnableJavaScript, saved_fake_file_access_.get(),
+      &saved_document_, &saved_avail_, &saved_form_handle_));
   return saved_document_;
 }
 
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index c1f1844..4923180 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -33,6 +33,9 @@
                      public FPDF_FORMFILLINFO,
                      public FPDF_FILEWRITE {
  public:
+  enum class LinearizeOption { kDefaultLinearize, kMustLinearize };
+  enum class JavaScriptOption { kDisableJavaScript, kEnableJavaScript };
+
   class Delegate {
    public:
     virtual ~Delegate() {}
@@ -85,19 +88,22 @@
   bool CreateEmptyDocument();
 
   // Open the document specified by |filename|, and create its form fill
-  // environment, or return false on failure.
-  // The filename is relative to the test data directory where we store all the
-  // test files.
-  // |password| can be nullptr if there is none.
+  // environment, or return false on failure. The |filename| is relative to
+  // the test data directory where we store all the test files. |password| can
+  // be nullptr if the file is not password protected. If |javascript_opts|
+  // is kDisableJavascript, then the document will be given stubs in place
+  // of the real JS engine.
   virtual bool OpenDocumentWithOptions(const std::string& filename,
                                        const char* password,
-                                       bool must_linearize);
+                                       LinearizeOption linearize_option,
+                                       JavaScriptOption javascript_option);
 
   // Variants provided for convenience.
   bool OpenDocument(const std::string& filename);
   bool OpenDocumentLinearized(const std::string& filename);
   bool OpenDocumentWithPassword(const std::string& filename,
                                 const char* password);
+  bool OpenDocumentWithoutJavaScript(const std::string& filename);
 
   // Perform JavaScript actions that are to run at document open time.
   void DoOpenActions();
@@ -154,13 +160,15 @@
   using PageNumberToHandleMap = std::map<int, FPDF_PAGE>;
 
   bool OpenDocumentHelper(const char* password,
-                          bool must_linearize,
+                          LinearizeOption linearize_option,
+                          JavaScriptOption javascript_option,
                           FakeFileAccess* network_simulator,
                           FPDF_DOCUMENT* document,
                           FPDF_AVAIL* avail,
                           FPDF_FORMHANDLE* form_handle);
 
-  FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc);
+  FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc,
+                                           JavaScriptOption javascript_option);
 
   // Return the hash of |bitmap|.
   static std::string HashBitmap(FPDF_BITMAP bitmap);
diff --git a/testing/xfa_js_embedder_test.cpp b/testing/xfa_js_embedder_test.cpp
index 0fe4b7a..995c0ed 100644
--- a/testing/xfa_js_embedder_test.cpp
+++ b/testing/xfa_js_embedder_test.cpp
@@ -47,21 +47,24 @@
   return UnderlyingFromFPDFDocument(document())->GetXFADoc()->GetXFADoc();
 }
 
-bool XFAJSEmbedderTest::OpenDocumentWithOptions(const std::string& filename,
-                                                const char* password,
-                                                bool must_linearize) {
-  if (!EmbedderTest::OpenDocumentWithOptions(filename, password,
-                                             must_linearize))
+bool XFAJSEmbedderTest::OpenDocumentWithOptions(
+    const std::string& filename,
+    const char* password,
+    LinearizeOption linearize_option,
+    JavaScriptOption javascript_option) {
+  // JS required for XFA.
+  ASSERT(javascript_option == JavaScriptOption::kEnableJavaScript);
+  if (!EmbedderTest::OpenDocumentWithOptions(
+          filename, password, linearize_option, javascript_option)) {
     return false;
-
+  }
   script_context_ = GetXFADocument()->GetScriptContext();
   return true;
 }
 
 bool XFAJSEmbedderTest::Execute(const ByteStringView& input) {
-  if (ExecuteHelper(input)) {
+  if (ExecuteHelper(input))
     return true;
-  }
 
   CFXJSE_Value msg(GetIsolate());
   value_->GetObjectPropertyByIdx(1, &msg);
diff --git a/testing/xfa_js_embedder_test.h b/testing/xfa_js_embedder_test.h
index 0ddb02c..73487d2 100644
--- a/testing/xfa_js_embedder_test.h
+++ b/testing/xfa_js_embedder_test.h
@@ -27,7 +27,8 @@
   void TearDown() override;
   bool OpenDocumentWithOptions(const std::string& filename,
                                const char* password,
-                               bool must_linearize) override;
+                               LinearizeOption linearize_option,
+                               JavaScriptOption javascript_option) override;
 
   v8::Isolate* GetIsolate() const { return isolate_; }
   CXFA_Document* GetXFADocument();