Add unit tests for FPDF_BSTR.

Then fix the bug uncovered by tests when length == 0.

- Update comments to explain that "Byte String" isn't exactly correct.
- Rename variables to be saner.

Change-Id: I8a232b221dc6a7342bf838a66dfea3cc69d6de2f
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/62911
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index 73117e1..3c1b39c 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -111,6 +111,7 @@
     "fpdf_doc_unittest.cpp",
     "fpdf_edit_unittest.cpp",
     "fpdf_editimg_unittest.cpp",
+    "fpdf_view_unittest.cpp",
   ]
   deps = [
     ":fpdfsdk",
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index a4a70f4..db6f407 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -1074,49 +1074,49 @@
 #endif  // PDF_ENABLE_V8
 
 #ifdef PDF_ENABLE_XFA
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* str) {
-  if (!str)
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr) {
+  if (!bstr)
     return -1;
 
-  memset(str, 0, sizeof(FPDF_BSTR));
+  bstr->str = nullptr;
+  bstr->len = 0;
   return 0;
 }
 
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* str,
-                                                    FPDF_LPCSTR bstr,
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
+                                                    FPDF_LPCSTR cstr,
                                                     int length) {
-  if (!str || !bstr || !length)
+  if (!bstr || !cstr)
     return -1;
 
   if (length == -1)
-    length = strlen(bstr);
+    length = strlen(cstr);
 
   if (length == 0) {
-    FPDF_BStr_Clear(str);
+    FPDF_BStr_Clear(bstr);
     return 0;
   }
 
-  if (str->str && str->len < length)
-    str->str = FX_Realloc(char, str->str, length + 1);
-  else if (!str->str)
-    str->str = FX_Alloc(char, length + 1);
+  if (bstr->str && bstr->len < length)
+    bstr->str = FX_Realloc(char, bstr->str, length + 1);
+  else if (!bstr->str)
+    bstr->str = FX_Alloc(char, length + 1);
 
-  str->str[length] = 0;
-  memcpy(str->str, bstr, length);
-  str->len = length;
-
+  bstr->str[length] = 0;
+  memcpy(bstr->str, cstr, length);
+  bstr->len = length;
   return 0;
 }
 
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* str) {
-  if (!str)
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr) {
+  if (!bstr)
     return -1;
 
-  if (str->str) {
-    FX_Free(str->str);
-    str->str = nullptr;
+  if (bstr->str) {
+    FX_Free(bstr->str);
+    bstr->str = nullptr;
   }
-  str->len = 0;
+  bstr->len = 0;
   return 0;
 }
 #endif  // PDF_ENABLE_XFA
diff --git a/fpdfsdk/fpdf_view_unittest.cpp b/fpdfsdk/fpdf_view_unittest.cpp
new file mode 100644
index 0000000..c08c49b
--- /dev/null
+++ b/fpdfsdk/fpdf_view_unittest.cpp
@@ -0,0 +1,56 @@
+// 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 "public/fpdfview.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#ifdef PDF_ENABLE_XFA
+TEST(FPDFView, BstrBadArgs) {
+  EXPECT_EQ(-1, FPDF_BStr_Init(nullptr));
+  EXPECT_EQ(-1, FPDF_BStr_Set(nullptr, "clams", -1));
+  EXPECT_EQ(-1, FPDF_BStr_Clear(nullptr));
+}
+
+TEST(FPDFView, BstrEmpty) {
+  FPDF_BSTR bst;
+  EXPECT_EQ(0, FPDF_BStr_Init(&bst));
+  EXPECT_FALSE(bst.str);
+  EXPECT_FALSE(bst.len);
+  EXPECT_EQ(0, FPDF_BStr_Clear(&bst));
+}
+
+TEST(FPDFView, BstrNormal) {
+  FPDF_BSTR bst;
+  EXPECT_EQ(0, FPDF_BStr_Init(&bst));
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", -1));
+  EXPECT_STREQ("clams", bst.str);
+  EXPECT_EQ(5, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Clear(&bst));
+  EXPECT_FALSE(bst.str);
+  EXPECT_FALSE(bst.len);
+}
+
+TEST(FPDFView, BstrReassign) {
+  FPDF_BSTR bst;
+  EXPECT_EQ(0, FPDF_BStr_Init(&bst));
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 3));
+  EXPECT_STREQ("cla", bst.str);
+  EXPECT_EQ(3, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 5));
+  EXPECT_STREQ("clams", bst.str);
+  EXPECT_EQ(5, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 1));
+  EXPECT_STREQ("c", bst.str);
+  EXPECT_EQ(1, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Set(&bst, "clams", 0));
+  EXPECT_FALSE(bst.str);
+  EXPECT_EQ(0, bst.len);
+
+  EXPECT_EQ(0, FPDF_BStr_Clear(&bst));
+}
+#endif  // PDF_ENABLE_XFA
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 2b84f07..f7b99f4 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -107,13 +107,12 @@
 typedef const unsigned short* FPDF_WIDESTRING;
 
 #ifdef PDF_ENABLE_XFA
-// Structure for a byte string.
-// Note, a byte string commonly means a UTF-16LE formated string.
+// Structure for persisting a string beyond the duration of a callback.
+// Note: although represented as a char*, string may be interpreted as
+// a UTF-16LE formated string.
 typedef struct _FPDF_BSTR {
-  // String buffer.
-  char* str;
-  // Length of the string, in bytes.
-  int len;
+  char* str;  // String buffer, manipulate only with FPDF_BStr_* methods.
+  int len;    // Length of the string, in bytes.
 } FPDF_BSTR;
 #endif  // PDF_ENABLE_XFA
 
@@ -1193,18 +1192,18 @@
 
 #ifdef PDF_ENABLE_XFA
 // Function: FPDF_BStr_Init
-//          Helper function to initialize a byte string.
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* str);
+//          Helper function to initialize a FPDF_BSTR.
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr);
 
 // Function: FPDF_BStr_Set
-//          Helper function to set string data.
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* str,
-                                                    FPDF_LPCSTR bstr,
+//          Helper function to copy string data into the FPDF_BSTR.
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
+                                                    FPDF_LPCSTR cstr,
                                                     int length);
 
 // Function: FPDF_BStr_Clear
-//          Helper function to clear a byte string.
-FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* str);
+//          Helper function to clear a FPDF_BSTR.
+FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr);
 #endif  // PDF_ENABLE_XFA
 
 #ifdef __cplusplus