From 2b79782377b26cf6f5bd13ca34127711d5f38e8f Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Fri, 21 Oct 2016 13:29:15 +0200
Subject: [PATCH] [api-minor] Add basic support for `Launch` actions (issue
 1778, issue 3897, issue 6616)

In general we neither want, nor can, support arbitrary `Launch` actions. But in practice, all the cases we've seen so far just contains relative URLs to other PDF files. Building on PR 7689, we can thus at least support basic `Launch` actions.
---
 src/core/obj.js                    |  7 ++++++
 test/unit/annotation_layer_spec.js | 39 ++++++++++++++++++++++++++++++
 test/unit/api_spec.js              |  3 +++
 3 files changed, 49 insertions(+)

diff --git a/src/core/obj.js b/src/core/obj.js
index 7c47b6ea8..2e45ddfa2 100644
--- a/src/core/obj.js
+++ b/src/core/obj.js
@@ -171,6 +171,7 @@ var Catalog = (function CatalogClosure() {
         var outlineItem = {
           dest: data.dest,
           url: data.url,
+          unsafeUrl: data.unsafeUrl,
           newWindow: data.newWindow,
           title: stringToPDFString(title),
           color: rgbColor,
@@ -644,6 +645,12 @@ var Catalog = (function CatalogClosure() {
           dest = action.get('D');
           break;
 
+        case 'Launch':
+          // We neither want, nor can, support arbitrary 'Launch' actions.
+          // However, in practice they are mostly used for linking to other PDF
+          // files, which we thus attempt to support (utilizing `docBaseUrl`).
+          /* falls through */
+
         case 'GoToR':
           var urlDict = action.get('F');
           if (isDict(urlDict)) {
diff --git a/test/unit/annotation_layer_spec.js b/test/unit/annotation_layer_spec.js
index 16d461716..e0d5eddd8 100644
--- a/test/unit/annotation_layer_spec.js
+++ b/test/unit/annotation_layer_spec.js
@@ -505,6 +505,45 @@ describe('Annotation layer', function() {
       expect(data.newWindow).toBeFalsy();
     });
 
+    it('should correctly parse a Launch action, where the FileSpec dict ' +
+       'contains a relative URL, with the "docBaseUrl" parameter specified',
+        function() {
+      var fileSpecDict = new Dict();
+      fileSpecDict.set('Type', Name.get('FileSpec'));
+      fileSpecDict.set('F', 'Part II/Part II.pdf');
+      fileSpecDict.set('UF', 'Part II/Part II.pdf');
+
+      var actionDict = new Dict();
+      actionDict.set('Type', Name.get('Action'));
+      actionDict.set('S', Name.get('Launch'));
+      actionDict.set('F', fileSpecDict);
+      actionDict.set('NewWindow', true);
+
+      var annotationDict = new Dict();
+      annotationDict.set('Type', Name.get('Annot'));
+      annotationDict.set('Subtype', Name.get('Link'));
+      annotationDict.set('A', actionDict);
+
+      var annotationRef = new Ref(88, 0);
+      var xref = new XRefMock([
+        { ref: annotationRef, data: annotationDict, }
+      ]);
+      var pdfManager = new PDFManagerMock({
+        docBaseUrl: 'http://www.example.com/test/pdfs/qwerty.pdf',
+      });
+
+      var annotation = annotationFactory.create(xref, annotationRef,
+                                                pdfManager);
+      var data = annotation.data;
+      expect(data.annotationType).toEqual(AnnotationType.LINK);
+
+      expect(data.url).toEqual(
+        new URL('http://www.example.com/test/pdfs/Part II/Part II.pdf').href);
+      expect(data.unsafeUrl).toEqual('Part II/Part II.pdf');
+      expect(data.dest).toBeUndefined();
+      expect(data.newWindow).toEqual(true);
+    });
+
     it('should correctly parse a Named action', function() {
       var actionDict = new Dict();
       actionDict.set('Type', Name.get('Action'));
diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js
index 45d484332..663193b11 100644
--- a/test/unit/api_spec.js
+++ b/test/unit/api_spec.js
@@ -619,6 +619,8 @@ describe('api', function() {
         expect(outlineItem.title).toEqual('Chapter 1');
         expect(outlineItem.dest instanceof Array).toEqual(true);
         expect(outlineItem.url).toEqual(null);
+        expect(outlineItem.unsafeUrl).toBeUndefined();
+        expect(outlineItem.newWindow).toBeUndefined();
 
         expect(outlineItem.bold).toEqual(true);
         expect(outlineItem.italic).toEqual(false);
@@ -644,6 +646,7 @@ describe('api', function() {
           expect(typeof outlineItemTwo.title).toEqual('string');
           expect(outlineItemTwo.dest).toEqual(null);
           expect(outlineItemTwo.url).toEqual('http://google.com/');
+          expect(outlineItemTwo.unsafeUrl).toEqual('http://google.com');
           expect(outlineItemTwo.newWindow).toBeUndefined();
 
           var outlineItemOne = outline[1];