1// Copyright 2016 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fpdfdoc/cpdf_action.h"
8
9#include "core/fpdfapi/parser/cpdf_array.h"
10#include "core/fpdfapi/parser/cpdf_document.h"
11#include "core/fpdfdoc/cpdf_filespec.h"
12#include "core/fpdfdoc/cpdf_nametree.h"
13
14namespace {
15
16const char* const g_sATypes[] = {
17    "Unknown",     "GoTo",       "GoToR",     "GoToE",      "Launch",
18    "Thread",      "URI",        "Sound",     "Movie",      "Hide",
19    "Named",       "SubmitForm", "ResetForm", "ImportData", "JavaScript",
20    "SetOCGState", "Rendition",  "Trans",     "GoTo3DView", nullptr};
21
22}  // namespace
23
24CPDF_Action::CPDF_Action(CPDF_Dictionary* pDict) : m_pDict(pDict) {}
25
26CPDF_Action::CPDF_Action(const CPDF_Action& that) = default;
27
28CPDF_Action::~CPDF_Action() {}
29
30CPDF_Dest CPDF_Action::GetDest(CPDF_Document* pDoc) const {
31  if (!m_pDict)
32    return CPDF_Dest();
33
34  ByteString type = m_pDict->GetStringFor("S");
35  if (type != "GoTo" && type != "GoToR")
36    return CPDF_Dest();
37
38  CPDF_Object* pDest = m_pDict->GetDirectObjectFor("D");
39  if (!pDest)
40    return CPDF_Dest();
41  if (pDest->IsString() || pDest->IsName()) {
42    CPDF_NameTree name_tree(pDoc, "Dests");
43    return CPDF_Dest(name_tree.LookupNamedDest(pDoc, pDest->GetUnicodeText()));
44  }
45  if (CPDF_Array* pArray = pDest->AsArray())
46    return CPDF_Dest(pArray);
47
48  return CPDF_Dest();
49}
50
51CPDF_Action::ActionType CPDF_Action::GetType() const {
52  if (!m_pDict)
53    return Unknown;
54
55  ByteString csType = m_pDict->GetStringFor("S");
56  if (csType.IsEmpty())
57    return Unknown;
58
59  for (int i = 0; g_sATypes[i]; ++i) {
60    if (csType == g_sATypes[i])
61      return static_cast<ActionType>(i);
62  }
63  return Unknown;
64}
65
66WideString CPDF_Action::GetFilePath() const {
67  ByteString type = m_pDict->GetStringFor("S");
68  if (type != "GoToR" && type != "Launch" && type != "SubmitForm" &&
69      type != "ImportData") {
70    return WideString();
71  }
72
73  CPDF_Object* pFile = m_pDict->GetDirectObjectFor("F");
74  if (pFile)
75    return CPDF_FileSpec(pFile).GetFileName();
76
77  if (type == "Launch") {
78    CPDF_Dictionary* pWinDict = m_pDict->GetDictFor("Win");
79    if (pWinDict) {
80      return WideString::FromLocal(pWinDict->GetStringFor("F").AsStringView());
81    }
82  }
83  return WideString();
84}
85
86ByteString CPDF_Action::GetURI(const CPDF_Document* pDoc) const {
87  ByteString csURI;
88  if (!m_pDict)
89    return csURI;
90  if (m_pDict->GetStringFor("S") != "URI")
91    return csURI;
92
93  csURI = m_pDict->GetStringFor("URI");
94  const CPDF_Dictionary* pRoot = pDoc->GetRoot();
95  CPDF_Dictionary* pURI = pRoot->GetDictFor("URI");
96  if (pURI) {
97    auto result = csURI.Find(":");
98    if (!result.has_value() || result.value() == 0)
99      csURI = pURI->GetStringFor("Base") + csURI;
100  }
101  return csURI;
102}
103
104WideString CPDF_Action::GetJavaScript() const {
105  WideString csJS;
106  if (!m_pDict)
107    return csJS;
108
109  CPDF_Object* pJS = m_pDict->GetDirectObjectFor("JS");
110  return pJS ? pJS->GetUnicodeText() : csJS;
111}
112
113size_t CPDF_Action::GetSubActionsCount() const {
114  if (!m_pDict || !m_pDict->KeyExist("Next"))
115    return 0;
116
117  CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
118  if (!pNext)
119    return 0;
120  if (pNext->IsDictionary())
121    return 1;
122  if (CPDF_Array* pArray = pNext->AsArray())
123    return pArray->GetCount();
124  return 0;
125}
126
127CPDF_Action CPDF_Action::GetSubAction(size_t iIndex) const {
128  if (!m_pDict || !m_pDict->KeyExist("Next"))
129    return CPDF_Action(nullptr);
130
131  CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
132  if (CPDF_Array* pArray = ToArray(pNext))
133    return CPDF_Action(pArray->GetDictAt(iIndex));
134  if (CPDF_Dictionary* pDict = ToDictionary(pNext)) {
135    if (iIndex == 0)
136      return CPDF_Action(pDict);
137  }
138  return CPDF_Action(nullptr);
139}
140