1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/common/api/extension_action/action_info.h"
#include <memory>
#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/icons/extension_icon_variants.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handler_helpers.h"
#include "extensions/common/manifest_handlers/icon_variants_handler.h"
namespace extensions {
namespace errors = manifest_errors;
namespace keys = manifest_keys;
namespace {
constexpr char kEnabled[] = "enabled";
constexpr char kDisabled[] = "disabled";
// The manifest data container for the ActionInfos for BrowserActions and
// PageActions.
struct ActionInfoData : public Extension::ManifestData {
explicit ActionInfoData(std::unique_ptr<ActionInfo> action_info);
~ActionInfoData() override;
// The action associated with the BrowserAction.
std::unique_ptr<ActionInfo> action_info;
};
ActionInfoData::ActionInfoData(std::unique_ptr<ActionInfo> info)
: action_info(std::move(info)) {}
ActionInfoData::~ActionInfoData() = default;
using extensions::diagnostics::icon_variants::Feature;
using extensions::diagnostics::icon_variants::Id;
using extensions::diagnostics::icon_variants::Severity;
// Returns the icon variants parsed from the `extension` manifest.
// Populates `error` if there are no icon variants.
ExtensionIconVariants GetIconVariants(const base::Value* value) {
ExtensionIconVariants icon_variants;
// Convert the input key into a list containing everything.
if (!value->is_list()) {
icon_variants.AddDiagnostic(Feature::kIconVariants,
Id::kIconVariantsKeyMustBeAList);
return icon_variants;
}
icon_variants.Parse(&value->GetList());
// Verify `icon_variants`, e.g. that at least one `icon_variant` is valid.
if (icon_variants.IsEmpty()) {
icon_variants.AddDiagnostic(Feature::kIconVariants,
Id::kIconVariantsInvalid);
}
return icon_variants;
}
} // namespace
ActionInfo::ActionInfo(Type type) : type(type), synthesized(false) {
switch (type) {
case ActionInfo::Type::kPage:
default_state = ActionInfo::DefaultState::kDisabled;
break;
case ActionInfo::Type::kBrowser:
case ActionInfo::Type::kAction:
default_state = ActionInfo::DefaultState::kEnabled;
break;
}
}
ActionInfo::ActionInfo(ActionInfo&& other) = default;
ActionInfo::ActionInfo(const ActionInfo& other) = default;
ActionInfo::~ActionInfo() = default;
// static
std::unique_ptr<ActionInfo> ActionInfo::Load(
const Extension* extension,
Type type,
const base::Value::Dict& dict,
std::vector<InstallWarning>* install_warnings,
std::u16string* error) {
auto result = std::make_unique<ActionInfo>(type);
// Read the action `default_icon` (optional).
// The `default_icon` value can be either dictionary {icon size -> icon path}
// or non empty string value.
if (const base::Value* default_icon = dict.Find(keys::kActionDefaultIcon)) {
std::string default_icon_str;
if (default_icon->is_string())
default_icon_str = default_icon->GetString();
if (default_icon->is_dict()) {
std::vector<std::string> warnings;
if (!manifest_handler_helpers::LoadIconsFromDictionary(
default_icon->GetDict(), &result->default_icon, error,
&warnings)) {
return nullptr;
}
if (!warnings.empty()) {
install_warnings->reserve(install_warnings->size() + warnings.size());
std::string manifest_key = GetManifestKeyForActionType(type);
for (const auto& warning : warnings) {
install_warnings->emplace_back(warning, manifest_key,
keys::kActionDefaultIcon);
}
}
} else if (default_icon->is_string() &&
manifest_handler_helpers::NormalizeAndValidatePath(
&default_icon_str)) {
if (manifest_handler_helpers::IsIconMimeTypeValid(
base::FilePath::FromUTF8Unsafe(default_icon_str))) {
// Choose the most optimistic (highest) icon density regardless of the
// actual icon resolution, whatever that happens to be. Code elsewhere
// knows how to scale down to 19.
result->default_icon.Add(extension_misc::EXTENSION_ICON_GIGANTOR,
default_icon_str);
} else {
// Issue a warning and ignore this file. This is a warning and not a
// hard-error to preserve both backwards compatibility and potential
// future-compatibility if mime types change.
install_warnings->emplace_back(
manifest_errors::kInvalidActionDefaultIconMimeType,
GetManifestKeyForActionType(type), keys::kActionDefaultIcon);
}
} else {
*error = errors::kInvalidActionDefaultIcon;
return nullptr;
}
}
// Read the action title from `default_title` if present, `name` if not
// (both optional).
if (const base::Value* default_title = dict.Find(keys::kActionDefaultTitle)) {
if (!default_title->is_string()) {
*error = errors::kInvalidActionDefaultTitle;
return nullptr;
}
result->default_title = default_title->GetString();
}
// Read the action's default popup (optional).
if (const base::Value* default_popup = dict.Find(keys::kActionDefaultPopup)) {
const std::string* url_str = default_popup->GetIfString();
if (!url_str) {
*error = errors::kInvalidActionDefaultPopup;
return nullptr;
}
if (!url_str->empty()) {
GURL popup_url = extension->ResolveExtensionURL(*url_str);
if (!popup_url.is_valid()) {
*error = errors::kInvalidActionDefaultPopup;
return nullptr;
}
result->default_popup_url = popup_url;
} else {
// An empty string is treated as having no popup.
DCHECK(result->default_popup_url.is_empty());
}
}
if (const base::Value* default_state = dict.Find(keys::kActionDefaultState)) {
// The `default_state` key is only valid for TYPE_ACTION; throw an error for
// others.
if (type != ActionInfo::Type::kAction) {
*error = errors::kDefaultStateShouldNotBeSet;
return nullptr;
}
if (!default_state->is_string() ||
!(default_state->GetString() == kEnabled ||
default_state->GetString() == kDisabled)) {
*error = errors::kInvalidActionDefaultState;
return nullptr;
}
result->default_state = default_state->GetString() == kEnabled
? ActionInfo::DefaultState::kEnabled
: ActionInfo::DefaultState::kDisabled;
}
bool supports_icon_variants =
IconVariantsInfo::SupportsIconVariants(*extension);
const base::Value* icon_variants_value = dict.Find(keys::kIconVariants);
if (icon_variants_value) {
if (!supports_icon_variants) {
auto diagnostic = extensions::diagnostics::icon_variants::GetDiagnostic(
Feature::kIconVariants, Id::kIconVariantsNotEnabled);
install_warnings->emplace_back(diagnostic.message);
} else {
ExtensionIconVariants icon_variants =
GetIconVariants(icon_variants_value);
// Add any install warnings, handle errors, and then clear out
// diagnostics.
auto& diagnostics = icon_variants.get_diagnostics();
for (const auto& diagnostic : diagnostics) {
switch (diagnostic.severity) {
case Severity::kWarning:
// Add an install warning.
install_warnings->emplace_back(diagnostic.message);
break;
case Severity::kError:
// If any error exists, do not load the extension.
*error = base::UTF8ToUTF16(diagnostic.message);
return nullptr;
default:
NOTREACHED();
}
install_warnings->emplace_back(diagnostic.message);
}
diagnostics.clear();
result->icon_variants = std::move(icon_variants);
}
}
return result;
}
// static
const ActionInfo* ActionInfo::GetExtensionActionInfo(
const Extension* extension) {
const ActionInfoData* data =
static_cast<ActionInfoData*>(extension->GetManifestData(keys::kAction));
return data ? data->action_info.get() : nullptr;
}
// static
void ActionInfo::SetExtensionActionInfo(Extension* extension,
std::unique_ptr<ActionInfo> info) {
// Note: we store all actions (actions, browser actions, and page actions)
// under the same key for simplicity because they are mutually exclusive,
// and most callers shouldn't care about the type.
extension->SetManifestData(keys::kAction,
std::make_unique<ActionInfoData>(std::move(info)));
}
// static
const char* ActionInfo::GetManifestKeyForActionType(ActionInfo::Type type) {
const char* action_key = nullptr;
switch (type) {
case ActionInfo::Type::kBrowser:
action_key = manifest_keys::kBrowserAction;
break;
case ActionInfo::Type::kPage:
action_key = manifest_keys::kPageAction;
break;
case ActionInfo::Type::kAction:
action_key = manifest_keys::kAction;
break;
}
return action_key;
}
} // namespace extensions
|