Add javascript capabilities in pdf_xfa_fdp_fuzzer

Add support for simple javascript statements in pdf_xfa_fdp_fuzzer

Bug: chromium:1276950
Change-Id: I327e681b773e0824b43a472bdfa2f0a72c7586b0
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/88110
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
index 86d3139..de36fcd 100644
--- a/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
+++ b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
@@ -195,8 +195,8 @@
   FuzzedDataProvider* fdp_ = nullptr;
 };
 
-// Possible names of an XFA script function
-std::string GenXfaScriptFuncName(FuzzedDataProvider* data_provider) {
+// Possible names of an XFA FormCalc script function
+std::string GenXfaFormCalcScriptFuncName(FuzzedDataProvider* data_provider) {
   static const char* const kXfaScriptFuncs[] = {
       "Abs",       "Apr",        "At",           "Avg",          "Ceil",
       "Choose",    "Concat",     "Count",        "Cterm",        "Date",
@@ -648,17 +648,18 @@
 // Possible XFA attributes values
 std::string GenXfaTagValue(FuzzedDataProvider* data_provider) {
   static const char* const kXfaTagVals[] = {
-      "0",         "0pt",          "-1",
-      "123",       "1pt",          "203.2mm",
-      "22.1404mm", "255",          "256",
-      "321",       "5431.21mm",    "6.35mm",
-      "8in",       "8pt",          "application/x-javascript",
-      "bold",      "bold",         "consumeData",
-      "en_US",     "form1",        "initialize",
-      "italic",    "middle",       "name2",
-      "name3",     "name4",        "name5",
-      "Page1",     "RadioList[0]", "subform_1",
-      "tb",        "Verdana",      "Verdana",
+      "0",         "0pt",         "-1",
+      "123",       "1pt",         "203.2mm",
+      "22.1404mm", "255",         "256",
+      "321",       "5431.21mm",   "6.35mm",
+      "8in",       "8pt",         "application/x-javascript",
+      "bold",      "bold",        "change",
+      "click",     "consumeData", "docReady",
+      "en_US",     "form1",       "initialize",
+      "italic",    "middle",      "name2",
+      "name3",     "name4",       "name5",
+      "onEnter",   "Page1",       "RadioList[0]",
+      "subform_1", "tb",          "Verdana",
   };
 
   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
@@ -669,17 +670,22 @@
 // possible XFA attributes
 std::string GenXfaTagName(FuzzedDataProvider* data_provider) {
   static const char* const kXfaTagNames[] = {
-      "activity",    "activity",    "baselineShift",
-      "contentType", "h",           "id",
-      "layout",      "layout",      "leftInset",
-      "locale",      "long",        "marginLeft",
-      "marginRight", "marginRight", "mergeMode",
-      "name",        "ref",         "scriptTest",
-      "short",       "size",        "spaceAbove",
-      "spaceBelow",  "startNew",    "stock",
-      "tetIndent",   "timeStamp",   "typeface",
-      "uuid",        "vAlign",      "value",
-      "w",           "weight",      "x",
+      "activity",    "baselineShift",
+      "contentType", "h",
+      "id",          "layout",
+      "layout",      "leftInset",
+      "locale",      "long",
+      "marginLeft",  "marginRight",
+      "marginRight", "mergeMode",
+      "name",        "ref",
+      "scriptTest",  "short",
+      "size",        "spaceAbove",
+      "spaceBelow",  "startNew",
+      "stock",       "textIndent",
+      "timeStamp",   "typeface",
+      "uuid",        "vAlign",
+      "value",       "w",
+      "weight",      "x",
       "y",
   };
   size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
@@ -687,30 +693,158 @@
   return kXfaTagNames[elem_selector];
 }
 
-// Will create a simple XFA script that calls a single function.
-std::string GenXfacript(FuzzedDataProvider* data_provider) {
-  std::string xfa_string = GenXfaScriptFuncName(data_provider);
+// Will create a simple XFA FormCalc script that calls a single function.
+std::string GenXfaFormCalcScript(FuzzedDataProvider* data_provider) {
+  std::string xfa_string = GenXfaFormCalcScriptFuncName(data_provider);
   xfa_string += "(";
 
-  int num_params = data_provider->ConsumeIntegralInRange(0, 3);
-  // 0 case we do nothing.
-  if (num_params == 1) {
-    xfa_string += GenXfaScriptParam(data_provider);
-  } else if (num_params == 2) {
-    xfa_string += GenXfaScriptParam(data_provider);
-    xfa_string += ",";
-    xfa_string += GenXfaScriptParam(data_provider);
-  } else if (num_params == 3) {
-    xfa_string += GenXfaScriptParam(data_provider);
-    xfa_string += ",";
-    xfa_string += GenXfaScriptParam(data_provider);
-    xfa_string += ",";
+  // Generate parameters
+  size_t num_params = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
+  for (size_t i = 0; i < num_params; i++) {
+    if (i != 0) {
+      xfa_string += ",";
+    }
     xfa_string += GenXfaScriptParam(data_provider);
   }
   xfa_string += ")";
   return xfa_string;
 }
 
+// XFA Javascript logic
+std::string GenXfaName(FuzzedDataProvider* data_provider) {
+  return "name" + std::to_string(data_provider->ConsumeIntegralInRange(0, 25));
+}
+
+std::string GetXfaJSPrimitiveType(FuzzedDataProvider* data_provider) {
+  return GenXfaScriptParam(data_provider);
+}
+
+std::string GenXfaJSRValue(FuzzedDataProvider* data_provider) {
+  if (data_provider->ConsumeBool()) {
+    return GenXfaScriptParam(data_provider);
+  }
+
+  std::string xfa_string;
+  if (data_provider->ConsumeBool()) {
+    xfa_string += "xfa.form.";
+  }
+
+  // Handle the possibility of nested names
+  size_t num_nests = data_provider->ConsumeIntegralInRange<size_t>(1, 3);
+  for (size_t i = 0; i < num_nests; i++) {
+    if (i != 0) {
+      xfa_string += ".";
+    }
+    xfa_string += GenXfaName(data_provider);
+  }
+  return MaybeQuote(data_provider, xfa_string);
+}
+
+std::string GenXfaJSAssignment(FuzzedDataProvider* data_provider) {
+  return GenXfaName(data_provider) + " = " + GenXfaJSRValue(data_provider);
+}
+
+std::string GenXfaJSMethodCall(FuzzedDataProvider* data_provider) {
+  static const char* const kXfaJSFuncs[] = {
+      "addItem",
+      "boundItem",
+      "clearItems",
+      "deleteItem",
+      "execCalculate",
+      "execEvent",
+      "execInitialize",
+      "execValidate",
+      "getDisplayItem",
+      "getItemState",
+      "getSaveItem",
+      "exec.form.formNodes",
+      "exec.form.recalculate",
+      "setItemState",
+      "xfa.container.getDelta",
+      "xfa.container.getDeltas",
+      "xfa.event.emit",
+      "xfa.event.reset",
+      "xfa.form.execCalculat",
+      "xfa.form.execInitialize",
+      "xfa.form.execValidate",
+      "xfa.form.remerge",
+      "xfa.host.beep",
+      "xfa.host.documentCountInBatch",
+      "xfa.host.documentInBatch",
+      "xfa.host.exportData",
+      "xfa.host.getFocus",
+      "xfa.host.gotoURL",
+      "xfa.host.importData",
+      "xfa.host.messageBox",
+      "xfa.host.openList",
+      "xfa.host.pageDown",
+      "xfa.host.pageUp",
+      "xfa.host.print",
+      "xfa.host.resetData",
+      "xfa.host.setFocus",
+      "xfa.host.response",
+      "xfa.resolveNode",
+  };
+
+  std::string xfa_string = data_provider->PickValueInArray(kXfaJSFuncs);
+  xfa_string += "(";
+
+  // Get the params
+  size_t param_count = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
+  for (size_t i = 0; i < param_count; i++) {
+    if (i != 0) {
+      xfa_string += ",";
+    }
+    xfa_string += GenXfaJSRValue(data_provider);
+  }
+  xfa_string += ")";
+  return xfa_string;
+}
+
+// This is a simple generator of xfa-based javascript. The function creates
+// simple javascript statements that are related to XFA logic and the goal is
+// not to create fully-fleged javascript programs but rather use simple
+// statements to ensure XFA code is covered.
+enum XFAJSStatement {
+  kAssignment = 0,
+  kJSMethodCall,
+  kJSObjectCall,
+  kMaxValue = kJSObjectCall
+};
+
+std::string GenXfaJSScript(FuzzedDataProvider* data_provider) {
+  std::string xfa_string;
+
+  size_t num_stmts = data_provider->ConsumeIntegralInRange<size_t>(1, 10);
+  for (size_t i = 0; i < num_stmts; i++) {
+    XFAJSStatement stmt = data_provider->ConsumeEnum<XFAJSStatement>();
+    switch (stmt) {
+      case kAssignment:
+        xfa_string += GenXfaJSAssignment(data_provider);
+        break;
+      case kJSMethodCall:
+        xfa_string += GenXfaJSMethodCall(data_provider);
+        break;
+      case kJSObjectCall:
+        xfa_string += GenXfaName(data_provider);
+        xfa_string += ".";
+        xfa_string += GenXfaJSMethodCall(data_provider);
+        break;
+    }
+    xfa_string += ";\n";
+  }
+  return xfa_string;
+}
+
+std::string GenXfacript(FuzzedDataProvider* data_provider) {
+  // Determine if this should be a FormCalc script or Javascript, 50/50 chance
+  // for each.
+  if (data_provider->ConsumeBool()) {
+    return GenXfaFormCalcScript(data_provider);
+  }
+  return GenXfaJSScript(data_provider);
+}
+
 // Will create a single XFA attributes, with both lhs and rhs.
 std::string getXfaElemAttributes(FuzzedDataProvider* data_provider) {
   // Generate a set of tags, and a set of values for the tags.