blob: 10e338583d521d3431632341ca178b538bcaeb3a [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/extensions/api/extension_action/action_info.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/extensions/api/commands/commands_handler.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_manifest_constants.h"
#include "chrome/common/extensions/manifest_handler_helpers.h"
#include "extensions/common/error_utils.h"
namespace errors = extension_manifest_errors;
namespace keys = extensions::manifest_keys;
namespace extensions {
namespace {
// The manifest data container for the ActionInfos for BrowserActions and
// ScriptBadges.
struct ActionInfoData : public Extension::ManifestData {
explicit ActionInfoData(ActionInfo* action_info);
virtual ~ActionInfoData();
// The action associated with the BrowserAction or ScriptBadge.
// This is never NULL for ScriptBadge.
scoped_ptr<ActionInfo> action_info;
};
ActionInfoData::ActionInfoData(ActionInfo* info) : action_info(info) {
}
ActionInfoData::~ActionInfoData() {
}
static const ActionInfo* GetActionInfo(const Extension* extension,
const std::string& key) {
ActionInfoData* data = static_cast<ActionInfoData*>(
extension->GetManifestData(key));
return data ? data->action_info.get() : NULL;
}
} // namespace
ActionInfo::ActionInfo() {
}
ActionInfo::~ActionInfo() {
}
// static
scoped_ptr<ActionInfo> ActionInfo::Load(const Extension* extension,
const base::DictionaryValue* dict,
string16* error) {
scoped_ptr<ActionInfo> result(new ActionInfo());
if (extension->manifest_version() == 1) {
// kPageActionIcons is obsolete, and used by very few extensions. Continue
// loading it, but only take the first icon as the default_icon path.
const base::ListValue* icons = NULL;
if (dict->HasKey(keys::kPageActionIcons) &&
dict->GetList(keys::kPageActionIcons, &icons)) {
for (base::ListValue::const_iterator iter = icons->begin();
iter != icons->end(); ++iter) {
std::string path;
if (!(*iter)->GetAsString(&path) ||
!manifest_handler_helpers::NormalizeAndValidatePath(&path)) {
*error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
return scoped_ptr<ActionInfo>();
}
result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path);
break;
}
}
std::string id;
if (dict->HasKey(keys::kPageActionId)) {
if (!dict->GetString(keys::kPageActionId, &id)) {
*error = ASCIIToUTF16(errors::kInvalidPageActionId);
return scoped_ptr<ActionInfo>();
}
result->id = id;
}
}
// Read the page action |default_icon| (optional).
// The |default_icon| value can be either dictionary {icon size -> icon path}
// or non empty string value.
if (dict->HasKey(keys::kPageActionDefaultIcon)) {
const DictionaryValue* icons_value = NULL;
std::string default_icon;
if (dict->GetDictionary(keys::kPageActionDefaultIcon, &icons_value)) {
if (!manifest_handler_helpers::LoadIconsFromDictionary(
icons_value,
extension_misc::kExtensionActionIconSizes,
extension_misc::kNumExtensionActionIconSizes,
&result->default_icon,
error)) {
return scoped_ptr<ActionInfo>();
}
} else if (dict->GetString(keys::kPageActionDefaultIcon, &default_icon) &&
manifest_handler_helpers::NormalizeAndValidatePath(
&default_icon)) {
result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION,
default_icon);
} else {
*error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
return scoped_ptr<ActionInfo>();
}
}
// Read the page action title from |default_title| if present, |name| if not
// (both optional).
if (dict->HasKey(keys::kPageActionDefaultTitle)) {
if (!dict->GetString(keys::kPageActionDefaultTitle,
&result->default_title)) {
*error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
return scoped_ptr<ActionInfo>();
}
} else if (extension->manifest_version() == 1 && dict->HasKey(keys::kName)) {
if (!dict->GetString(keys::kName, &result->default_title)) {
*error = ASCIIToUTF16(errors::kInvalidPageActionName);
return scoped_ptr<ActionInfo>();
}
}
// Read the action's |popup| (optional).
const char* popup_key = NULL;
if (dict->HasKey(keys::kPageActionDefaultPopup))
popup_key = keys::kPageActionDefaultPopup;
if (extension->manifest_version() == 1 &&
dict->HasKey(keys::kPageActionPopup)) {
if (popup_key) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidPageActionOldAndNewKeys,
keys::kPageActionDefaultPopup,
keys::kPageActionPopup);
return scoped_ptr<ActionInfo>();
}
popup_key = keys::kPageActionPopup;
}
if (popup_key) {
const DictionaryValue* popup = NULL;
std::string url_str;
if (dict->GetString(popup_key, &url_str)) {
// On success, |url_str| is set. Nothing else to do.
} else if (extension->manifest_version() == 1 &&
dict->GetDictionary(popup_key, &popup)) {
if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidPageActionPopupPath, "<missing>");
return scoped_ptr<ActionInfo>();
}
} else {
*error = ASCIIToUTF16(errors::kInvalidPageActionPopup);
return scoped_ptr<ActionInfo>();
}
if (!url_str.empty()) {
// An empty string is treated as having no popup.
result->default_popup_url = Extension::GetResourceURL(extension->url(),
url_str);
if (!result->default_popup_url.is_valid()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kInvalidPageActionPopupPath, url_str);
return scoped_ptr<ActionInfo>();
}
} else {
DCHECK(result->default_popup_url.is_empty())
<< "Shouldn't be possible for the popup to be set.";
}
}
return result.Pass();
}
// static
const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) {
return GetActionInfo(extension, keys::kBrowserAction);
}
const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) {
return GetActionInfo(extension, keys::kPageAction);
}
// static
const ActionInfo* ActionInfo::GetScriptBadgeInfo(const Extension* extension) {
return GetActionInfo(extension, keys::kScriptBadge);
}
// static
const ActionInfo* ActionInfo::GetSystemIndicatorInfo(
const Extension* extension) {
return GetActionInfo(extension, keys::kSystemIndicator);
}
// static
void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) {
extension->SetManifestData(keys::kBrowserAction,
new ActionInfoData(info));
}
// static
void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) {
extension->SetManifestData(keys::kPageAction,
new ActionInfoData(info));
}
// static
void ActionInfo::SetScriptBadgeInfo(Extension* extension, ActionInfo* info) {
extension->SetManifestData(keys::kScriptBadge,
new ActionInfoData(info));
}
// static
void ActionInfo::SetSystemIndicatorInfo(Extension* extension,
ActionInfo* info) {
extension->SetManifestData(keys::kSystemIndicator, new ActionInfoData(info));
}
// static
bool ActionInfo::IsVerboseInstallMessage(const Extension* extension) {
const ActionInfo* page_action_info = GetPageActionInfo(extension);
return page_action_info &&
(CommandsInfo::GetPageActionCommand(extension) ||
!page_action_info->default_icon.empty());
}
} // namespace extensions