Add support to Document::gotoNamedDest method.

Patch implements the Document's API gotoNamedDest, which is
part of the PDF specification [1], page 129, with the following
(short) description:

"Use this method to go to a named destination within the
PDF document".

[1] http://partners.adobe.com/public/developer/en/acrobat/sdk/5186AcroJS.pdf

"Named destination" is a common concept in the PDF world.
It can be used together with PDF's Links, Annotations, Bookmarks
and OpenActions, as well as an action per se, in case "this.gotoNamedDest"
is called directly.

Note that the implementation makes use of the existing hook
CPDFDoc_Environment::FFI_DoGoToAction, which ends up calling
out the embedder to actually handle it.
In case of Chromium, for instance, it calls PDFiumEngine::Form_DoGoToAction
which only handles for now the "page" property of the "destination".
Other properties, including zoom level, and scroll position
are ignored for the moment.

BUG=pdfium:492

Review-Url: https://codereview.chromium.org/2221823003
diff --git a/fpdfsdk/javascript/Document.cpp b/fpdfsdk/javascript/Document.cpp
index 161a5d0..8175fba 100644
--- a/fpdfsdk/javascript/Document.cpp
+++ b/fpdfsdk/javascript/Document.cpp
@@ -10,9 +10,11 @@
 
 #include "core/fpdfapi/fpdf_font/include/cpdf_font.h"
 #include "core/fpdfapi/fpdf_page/include/cpdf_page.h"
+#include "core/fpdfapi/fpdf_parser/include/cpdf_array.h"
 #include "core/fpdfapi/fpdf_parser/include/cpdf_document.h"
 #include "core/fpdfapi/fpdf_parser/include/fpdf_parser_decode.h"
 #include "core/fpdfdoc/include/cpdf_interform.h"
+#include "core/fpdfdoc/include/cpdf_nametree.h"
 #include "fpdfsdk/include/fsdk_mgr.h"
 #include "fpdfsdk/javascript/Field.h"
 #include "fpdfsdk/javascript/Icon.h"
@@ -124,6 +126,7 @@
 JS_STATIC_METHOD_ENTRY(getPageNumWords)
 JS_STATIC_METHOD_ENTRY(getPrintParams)
 JS_STATIC_METHOD_ENTRY(getURL)
+JS_STATIC_METHOD_ENTRY(gotoNamedDest)
 JS_STATIC_METHOD_ENTRY(importAnFDF)
 JS_STATIC_METHOD_ENTRY(importAnXFDF)
 JS_STATIC_METHOD_ENTRY(importTextData)
@@ -1466,6 +1469,52 @@
   return TRUE;
 }
 
+FX_BOOL Document::gotoNamedDest(IJS_Context* cc,
+                                const std::vector<CJS_Value>& params,
+                                CJS_Value& vRet,
+                                CFX_WideString& sError) {
+  CJS_Context* context = (CJS_Context*)cc;
+  if (params.size() != 1) {
+    sError = JSGetStringFromID(context, IDS_STRING_JSPARAMERROR);
+    return FALSE;
+  }
+
+  CPDF_Document* pDocument = m_pDocument->GetPDFDocument();
+  if (!pDocument)
+    return FALSE;
+
+  CFX_WideString wideName = params[0].ToCFXWideString();
+  CFX_ByteString utf8Name = wideName.UTF8Encode();
+
+  CPDF_NameTree nameTree(pDocument, "Dests");
+  CPDF_Array* destArray = nameTree.LookupNamedDest(pDocument, utf8Name);
+  if (!destArray)
+    return FALSE;
+
+  CPDF_Dest dest(destArray);
+  const CPDF_Array* arrayObject = ToArray(dest.GetObject());
+
+  std::unique_ptr<float[]> scrollPositionArray;
+  int scrollPositionArraySize = 0;
+
+  if (arrayObject) {
+    scrollPositionArray.reset(new float[arrayObject->GetCount()]);
+    int j = 0;
+    for (size_t i = 2; i < arrayObject->GetCount(); i++)
+      scrollPositionArray[j++] = arrayObject->GetFloatAt(i);
+    scrollPositionArraySize = j;
+  }
+
+  CJS_Runtime* runtime = context->GetJSRuntime();
+  runtime->BeginBlock();
+  CPDFDoc_Environment* pApp = m_pDocument->GetEnv();
+  pApp->FFI_DoGoToAction(dest.GetPageIndex(pDocument), dest.GetZoomMode(),
+                         scrollPositionArray.get(), scrollPositionArraySize);
+  runtime->EndBlock();
+
+  return TRUE;
+}
+
 void Document::AddDelayData(CJS_DelayData* pData) {
   m_DelayData.push_back(std::unique_ptr<CJS_DelayData>(pData));
 }
diff --git a/fpdfsdk/javascript/Document.h b/fpdfsdk/javascript/Document.h
index ae2d6c1..873f70e 100644
--- a/fpdfsdk/javascript/Document.h
+++ b/fpdfsdk/javascript/Document.h
@@ -207,6 +207,10 @@
                  const std::vector<CJS_Value>& params,
                  CJS_Value& vRet,
                  CFX_WideString& sError);
+  FX_BOOL gotoNamedDest(IJS_Context* cc,
+                        const std::vector<CJS_Value>& params,
+                        CJS_Value& vRet,
+                        CFX_WideString& sError);
   FX_BOOL importAnFDF(IJS_Context* cc,
                       const std::vector<CJS_Value>& params,
                       CJS_Value& vRet,
@@ -356,6 +360,7 @@
   JS_STATIC_METHOD(getPageNumWords, Document);
   JS_STATIC_METHOD(getPrintParams, Document);
   JS_STATIC_METHOD(getURL, Document);
+  JS_STATIC_METHOD(gotoNamedDest, Document);
   JS_STATIC_METHOD(importAnFDF, Document);
   JS_STATIC_METHOD(importAnXFDF, Document);
   JS_STATIC_METHOD(importTextData, Document);
diff --git a/testing/resources/javascript/document_methods.in b/testing/resources/javascript/document_methods.in
index 3cd3330..1c53dc2 100644
--- a/testing/resources/javascript/document_methods.in
+++ b/testing/resources/javascript/document_methods.in
@@ -214,6 +214,17 @@
   // TODO(tsepez): test success cases.
 }
 
+function testGotoNamedDest() {
+   // Method is present.
+   expect('typeof this.gotoNamedDest', 'function');
+
+   // Method needs exactly one argument.
+   expectError('this.gotoNamedDest()');
+   expectError('this.gotoNamedDest(1, 2)');
+
+   // TODO(tonikitoo): test success cases.
+}
+
 function testMailDoc() {
    // Method is present.
    expect('typeof this.mailDoc', 'function');
@@ -303,6 +314,7 @@
   testGetPageNthWordQuads();
   testGetPageNumWords();
   testGetPrintParams();
+  testGotoNamedDest();
   testMailDoc();
   testMailForm();
   testPrint();
diff --git a/testing/resources/javascript/document_methods_expected.txt b/testing/resources/javascript/document_methods_expected.txt
index 26f7b54..d66de8b 100644
--- a/testing/resources/javascript/document_methods_expected.txt
+++ b/testing/resources/javascript/document_methods_expected.txt
@@ -97,6 +97,9 @@
 Alert: PASS: this.getPageNumWords(-1) threw error Document.getPageNumWords: Incorrect parameter value.
 Alert: PASS: this.getPageNumWords(6) threw error Document.getPageNumWords: Incorrect parameter value.
 Alert: PASS: typeof this.getPrintParams = function
+Alert: PASS: typeof this.gotoNamedDest = function
+Alert: PASS: this.gotoNamedDest() threw error Document.gotoNamedDest: Incorrect number of parameters passed to function.
+Alert: PASS: this.gotoNamedDest(1, 2) threw error Document.gotoNamedDest: Incorrect number of parameters passed to function.
 Alert: PASS: typeof this.mailDoc = function
 Alert: PASS: typeof this.mailForm = function
 Alert: PASS: typeof this.print = function