action_info.cc revision f2477e01787aa58f445919b809d89e252beef54f
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 "base/memory/scoped_ptr.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/common/extensions/api/commands/commands_handler.h"
10#include "chrome/common/extensions/extension_constants.h"
11#include "chrome/common/extensions/manifest_handler_helpers.h"
12#include "extensions/common/error_utils.h"
13#include "extensions/common/extension.h"
14#include "extensions/common/manifest_constants.h"
15
16namespace extensions {
17
18namespace errors = manifest_errors;
19namespace keys = manifest_keys;
20
21namespace {
22
23// The manifest data container for the ActionInfos for BrowserActions and
24// ScriptBadges.
25struct ActionInfoData : public Extension::ManifestData {
26  explicit ActionInfoData(ActionInfo* action_info);
27  virtual ~ActionInfoData();
28
29  // The action associated with the BrowserAction or ScriptBadge.
30  // This is never NULL for ScriptBadge.
31  scoped_ptr<ActionInfo> action_info;
32};
33
34ActionInfoData::ActionInfoData(ActionInfo* info) : action_info(info) {
35}
36
37ActionInfoData::~ActionInfoData() {
38}
39
40static const ActionInfo* GetActionInfo(const Extension* extension,
41                                       const std::string& key) {
42  ActionInfoData* data = static_cast<ActionInfoData*>(
43      extension->GetManifestData(key));
44  return data ? data->action_info.get() : NULL;
45}
46
47}  // namespace
48
49ActionInfo::ActionInfo() {
50}
51
52ActionInfo::~ActionInfo() {
53}
54
55// static
56scoped_ptr<ActionInfo> ActionInfo::Load(const Extension* extension,
57                                        const base::DictionaryValue* dict,
58                                        string16* error) {
59  scoped_ptr<ActionInfo> result(new ActionInfo());
60
61  if (extension->manifest_version() == 1) {
62    // kPageActionIcons is obsolete, and used by very few extensions. Continue
63    // loading it, but only take the first icon as the default_icon path.
64    const base::ListValue* icons = NULL;
65    if (dict->HasKey(keys::kPageActionIcons) &&
66        dict->GetList(keys::kPageActionIcons, &icons)) {
67      base::ListValue::const_iterator iter = icons->begin();
68      std::string path;
69      if (iter == icons->end() ||
70          !(*iter)->GetAsString(&path) ||
71          !manifest_handler_helpers::NormalizeAndValidatePath(&path)) {
72        *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
73        return scoped_ptr<ActionInfo>();
74      }
75      result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path);
76    }
77
78    std::string id;
79    if (dict->HasKey(keys::kPageActionId)) {
80      if (!dict->GetString(keys::kPageActionId, &id)) {
81        *error = ASCIIToUTF16(errors::kInvalidPageActionId);
82        return scoped_ptr<ActionInfo>();
83      }
84      result->id = id;
85    }
86  }
87
88  // Read the page action |default_icon| (optional).
89  // The |default_icon| value can be either dictionary {icon size -> icon path}
90  // or non empty string value.
91  if (dict->HasKey(keys::kPageActionDefaultIcon)) {
92    const DictionaryValue* icons_value = NULL;
93    std::string default_icon;
94    if (dict->GetDictionary(keys::kPageActionDefaultIcon, &icons_value)) {
95      if (!manifest_handler_helpers::LoadIconsFromDictionary(
96              icons_value,
97              extension_misc::kExtensionActionIconSizes,
98              extension_misc::kNumExtensionActionIconSizes,
99              &result->default_icon,
100              error)) {
101        return scoped_ptr<ActionInfo>();
102      }
103    } else if (dict->GetString(keys::kPageActionDefaultIcon, &default_icon) &&
104               manifest_handler_helpers::NormalizeAndValidatePath(
105                   &default_icon)) {
106      result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION,
107                               default_icon);
108    } else {
109      *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
110      return scoped_ptr<ActionInfo>();
111    }
112  }
113
114  // Read the page action title from |default_title| if present, |name| if not
115  // (both optional).
116  if (dict->HasKey(keys::kPageActionDefaultTitle)) {
117    if (!dict->GetString(keys::kPageActionDefaultTitle,
118                         &result->default_title)) {
119      *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
120      return scoped_ptr<ActionInfo>();
121    }
122  } else if (extension->manifest_version() == 1 && dict->HasKey(keys::kName)) {
123    if (!dict->GetString(keys::kName, &result->default_title)) {
124      *error = ASCIIToUTF16(errors::kInvalidPageActionName);
125      return scoped_ptr<ActionInfo>();
126    }
127  }
128
129  // Read the action's |popup| (optional).
130  const char* popup_key = NULL;
131  if (dict->HasKey(keys::kPageActionDefaultPopup))
132    popup_key = keys::kPageActionDefaultPopup;
133
134  if (extension->manifest_version() == 1 &&
135      dict->HasKey(keys::kPageActionPopup)) {
136    if (popup_key) {
137      *error = ErrorUtils::FormatErrorMessageUTF16(
138          errors::kInvalidPageActionOldAndNewKeys,
139          keys::kPageActionDefaultPopup,
140          keys::kPageActionPopup);
141      return scoped_ptr<ActionInfo>();
142    }
143    popup_key = keys::kPageActionPopup;
144  }
145
146  if (popup_key) {
147    const DictionaryValue* popup = NULL;
148    std::string url_str;
149
150    if (dict->GetString(popup_key, &url_str)) {
151      // On success, |url_str| is set.  Nothing else to do.
152    } else if (extension->manifest_version() == 1 &&
153               dict->GetDictionary(popup_key, &popup)) {
154      if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) {
155        *error = ErrorUtils::FormatErrorMessageUTF16(
156            errors::kInvalidPageActionPopupPath, "<missing>");
157        return scoped_ptr<ActionInfo>();
158      }
159    } else {
160      *error = ASCIIToUTF16(errors::kInvalidPageActionPopup);
161      return scoped_ptr<ActionInfo>();
162    }
163
164    if (!url_str.empty()) {
165      // An empty string is treated as having no popup.
166      result->default_popup_url = Extension::GetResourceURL(extension->url(),
167                                                            url_str);
168      if (!result->default_popup_url.is_valid()) {
169        *error = ErrorUtils::FormatErrorMessageUTF16(
170            errors::kInvalidPageActionPopupPath, url_str);
171        return scoped_ptr<ActionInfo>();
172      }
173    } else {
174      DCHECK(result->default_popup_url.is_empty())
175          << "Shouldn't be possible for the popup to be set.";
176    }
177  }
178
179  return result.Pass();
180}
181
182// static
183const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) {
184  return GetActionInfo(extension, keys::kBrowserAction);
185}
186
187const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) {
188  return GetActionInfo(extension, keys::kPageAction);
189}
190
191// static
192const ActionInfo* ActionInfo::GetScriptBadgeInfo(const Extension* extension) {
193  return GetActionInfo(extension, keys::kScriptBadge);
194}
195
196// static
197const ActionInfo* ActionInfo::GetSystemIndicatorInfo(
198    const Extension* extension) {
199  return GetActionInfo(extension, keys::kSystemIndicator);
200}
201
202// static
203void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) {
204  extension->SetManifestData(keys::kBrowserAction,
205                             new ActionInfoData(info));
206}
207
208// static
209void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) {
210  extension->SetManifestData(keys::kPageAction,
211                             new ActionInfoData(info));
212}
213
214// static
215void ActionInfo::SetScriptBadgeInfo(Extension* extension, ActionInfo* info) {
216  extension->SetManifestData(keys::kScriptBadge,
217                             new ActionInfoData(info));
218}
219
220// static
221void ActionInfo::SetSystemIndicatorInfo(Extension* extension,
222                                        ActionInfo* info) {
223  extension->SetManifestData(keys::kSystemIndicator, new ActionInfoData(info));
224}
225
226// static
227bool ActionInfo::IsVerboseInstallMessage(const Extension* extension) {
228  const ActionInfo* page_action_info = GetPageActionInfo(extension);
229  return page_action_info &&
230      (CommandsInfo::GetPageActionCommand(extension) ||
231       !page_action_info->default_icon.empty());
232}
233
234}  // namespace extensions
235