1 // Copyright (c) 2012 The Chromium 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 #include "chrome/common/extensions/api/extension_action/action_info.h"
6 
7 #include <memory>
8 
9 #include "base/strings/utf_string_conversions.h"
10 #include "extensions/common/constants.h"
11 #include "extensions/common/error_utils.h"
12 #include "extensions/common/extension.h"
13 #include "extensions/common/manifest_constants.h"
14 #include "extensions/common/manifest_handler_helpers.h"
15 
16 namespace extensions {
17 
18 namespace errors = manifest_errors;
19 namespace keys = manifest_keys;
20 
21 namespace {
22 
23 constexpr char kEnabled[] = "enabled";
24 constexpr char kDisabled[] = "disabled";
25 
26 // The manifest data container for the ActionInfos for BrowserActions and
27 // PageActions.
28 struct ActionInfoData : public Extension::ManifestData {
29   explicit ActionInfoData(std::unique_ptr<ActionInfo> action_info);
30   ~ActionInfoData() override;
31 
32   // The action associated with the BrowserAction.
33   std::unique_ptr<ActionInfo> action_info;
34 };
35 
ActionInfoData(std::unique_ptr<ActionInfo> info)36 ActionInfoData::ActionInfoData(std::unique_ptr<ActionInfo> info)
37     : action_info(std::move(info)) {}
38 
~ActionInfoData()39 ActionInfoData::~ActionInfoData() {
40 }
41 
GetActionInfo(const Extension * extension,const std::string & key)42 static const ActionInfo* GetActionInfo(const Extension* extension,
43                                        const std::string& key) {
44   ActionInfoData* data = static_cast<ActionInfoData*>(
45       extension->GetManifestData(key));
46   return data ? data->action_info.get() : NULL;
47 }
48 
49 }  // namespace
50 
ActionInfo(Type type)51 ActionInfo::ActionInfo(Type type) : type(type), synthesized(false) {
52   switch (type) {
53     case TYPE_PAGE:
54       default_state = STATE_DISABLED;
55       break;
56     case TYPE_BROWSER:
57     case TYPE_ACTION:
58       default_state = STATE_ENABLED;
59       break;
60   }
61 }
62 
63 ActionInfo::ActionInfo(const ActionInfo& other) = default;
64 
~ActionInfo()65 ActionInfo::~ActionInfo() {
66 }
67 
68 // static
Load(const Extension * extension,Type type,const base::DictionaryValue * dict,base::string16 * error)69 std::unique_ptr<ActionInfo> ActionInfo::Load(const Extension* extension,
70                                              Type type,
71                                              const base::DictionaryValue* dict,
72                                              base::string16* error) {
73   auto result = std::make_unique<ActionInfo>(type);
74 
75   // Read the page action |default_icon| (optional).
76   // The |default_icon| value can be either dictionary {icon size -> icon path}
77   // or non empty string value.
78   if (dict->HasKey(keys::kActionDefaultIcon)) {
79     const base::DictionaryValue* icons_value = NULL;
80     std::string default_icon;
81     if (dict->GetDictionary(keys::kActionDefaultIcon, &icons_value)) {
82       if (!manifest_handler_helpers::LoadIconsFromDictionary(
83               icons_value, &result->default_icon, error)) {
84         return nullptr;
85       }
86     } else if (dict->GetString(keys::kActionDefaultIcon, &default_icon) &&
87                manifest_handler_helpers::NormalizeAndValidatePath(
88                    &default_icon)) {
89       // Choose the most optimistic (highest) icon density regardless of the
90       // actual icon resolution, whatever that happens to be. Code elsewhere
91       // knows how to scale down to 19.
92       result->default_icon.Add(extension_misc::EXTENSION_ICON_GIGANTOR,
93                                default_icon);
94     } else {
95       *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultIcon);
96       return nullptr;
97     }
98   }
99 
100   // Read the page action title from |default_title| if present, |name| if not
101   // (both optional).
102   if (dict->HasKey(keys::kActionDefaultTitle)) {
103     if (!dict->GetString(keys::kActionDefaultTitle, &result->default_title)) {
104       *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultTitle);
105       return nullptr;
106     }
107   }
108 
109   // Read the action's default popup (optional).
110   if (dict->HasKey(keys::kActionDefaultPopup)) {
111     std::string url_str;
112     if (!dict->GetString(keys::kActionDefaultPopup, &url_str)) {
113       *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultPopup);
114       return nullptr;
115     }
116 
117     if (!url_str.empty()) {
118       // An empty string is treated as having no popup.
119       result->default_popup_url = Extension::GetResourceURL(extension->url(),
120                                                             url_str);
121       if (!result->default_popup_url.is_valid()) {
122         *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultPopup);
123         return nullptr;
124       }
125     } else {
126       DCHECK(result->default_popup_url.is_empty())
127           << "Shouldn't be possible for the popup to be set.";
128     }
129   }
130 
131   if (dict->HasKey(keys::kActionDefaultState)) {
132     // The default_state key is only valid for TYPE_ACTION; throw an error for
133     // others.
134     if (type != TYPE_ACTION) {
135       *error = base::ASCIIToUTF16(errors::kDefaultStateShouldNotBeSet);
136       return nullptr;
137     }
138 
139     std::string default_state;
140     if (!dict->GetString(keys::kActionDefaultState, &default_state) ||
141         !(default_state == kEnabled || default_state == kDisabled)) {
142       *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultState);
143       return nullptr;
144     }
145     result->default_state = default_state == kEnabled
146                                 ? ActionInfo::STATE_ENABLED
147                                 : ActionInfo::STATE_DISABLED;
148   }
149 
150   return result;
151 }
152 
153 // static
GetAnyActionInfo(const Extension * extension)154 const ActionInfo* ActionInfo::GetAnyActionInfo(const Extension* extension) {
155   // TODO(devlin): Since all actions are mutually exclusive, we can store
156   // them all under the same key. For now, we don't do that because some callers
157   // need to differentiate between action types.
158   const ActionInfo* info = GetActionInfo(extension, keys::kBrowserAction);
159   if (info)
160     return info;
161   info = GetActionInfo(extension, keys::kPageAction);
162   if (info)
163     return info;
164   return GetActionInfo(extension, keys::kAction);
165 }
166 
167 // static
GetExtensionActionInfo(const Extension * extension)168 const ActionInfo* ActionInfo::GetExtensionActionInfo(
169     const Extension* extension) {
170   return GetActionInfo(extension, keys::kAction);
171 }
172 
173 // static
GetBrowserActionInfo(const Extension * extension)174 const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) {
175   return GetActionInfo(extension, keys::kBrowserAction);
176 }
177 
GetPageActionInfo(const Extension * extension)178 const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) {
179   return GetActionInfo(extension, keys::kPageAction);
180 }
181 
182 // static
SetExtensionActionInfo(Extension * extension,std::unique_ptr<ActionInfo> info)183 void ActionInfo::SetExtensionActionInfo(Extension* extension,
184                                         std::unique_ptr<ActionInfo> info) {
185   extension->SetManifestData(keys::kAction,
186                              std::make_unique<ActionInfoData>(std::move(info)));
187 }
188 
189 // static
SetBrowserActionInfo(Extension * extension,std::unique_ptr<ActionInfo> info)190 void ActionInfo::SetBrowserActionInfo(Extension* extension,
191                                       std::unique_ptr<ActionInfo> info) {
192   extension->SetManifestData(keys::kBrowserAction,
193                              std::make_unique<ActionInfoData>(std::move(info)));
194 }
195 
196 // static
SetPageActionInfo(Extension * extension,std::unique_ptr<ActionInfo> info)197 void ActionInfo::SetPageActionInfo(Extension* extension,
198                                    std::unique_ptr<ActionInfo> info) {
199   extension->SetManifestData(keys::kPageAction,
200                              std::make_unique<ActionInfoData>(std::move(info)));
201 }
202 
203 }  // namespace extensions
204