| // Copyright (c) 2013 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/manifest_handlers/theme_handler.h" |
| |
| #include "base/file_util.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace extensions { |
| |
| namespace keys = manifest_keys; |
| namespace errors = manifest_errors; |
| |
| namespace { |
| |
| bool LoadImages(const base::DictionaryValue* theme_value, |
| base::string16* error, |
| ThemeInfo* theme_info) { |
| const base::DictionaryValue* images_value = NULL; |
| if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { |
| // Validate that the images are all strings. |
| for (base::DictionaryValue::Iterator iter(*images_value); !iter.IsAtEnd(); |
| iter.Advance()) { |
| // The value may be a dictionary of scales and files paths. |
| // Or the value may be a file path, in which case a scale |
| // of 100% is assumed. |
| if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) { |
| const base::DictionaryValue* inner_value = NULL; |
| if (iter.value().GetAsDictionary(&inner_value)) { |
| for (base::DictionaryValue::Iterator inner_iter(*inner_value); |
| !inner_iter.IsAtEnd(); inner_iter.Advance()) { |
| if (!inner_iter.value().IsType(base::Value::TYPE_STRING)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidThemeImages); |
| return false; |
| } |
| } |
| } else { |
| *error = base::ASCIIToUTF16(errors::kInvalidThemeImages); |
| return false; |
| } |
| } else if (!iter.value().IsType(base::Value::TYPE_STRING)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidThemeImages); |
| return false; |
| } |
| } |
| theme_info->theme_images_.reset(images_value->DeepCopy()); |
| } |
| return true; |
| } |
| |
| bool LoadColors(const base::DictionaryValue* theme_value, |
| base::string16* error, |
| ThemeInfo* theme_info) { |
| const base::DictionaryValue* colors_value = NULL; |
| if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { |
| // Validate that the colors are RGB or RGBA lists. |
| for (base::DictionaryValue::Iterator iter(*colors_value); !iter.IsAtEnd(); |
| iter.Advance()) { |
| const base::ListValue* color_list = NULL; |
| double alpha = 0.0; |
| int color = 0; |
| // The color must be a list... |
| if (!iter.value().GetAsList(&color_list) || |
| // ... and either 3 items (RGB) or 4 (RGBA). |
| ((color_list->GetSize() != 3) && |
| ((color_list->GetSize() != 4) || |
| // For RGBA, the fourth item must be a real or int alpha value. |
| // Note that GetDouble() can get an integer value. |
| !color_list->GetDouble(3, &alpha))) || |
| // For both RGB and RGBA, the first three items must be ints (R,G,B). |
| !color_list->GetInteger(0, &color) || |
| !color_list->GetInteger(1, &color) || |
| !color_list->GetInteger(2, &color)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidThemeColors); |
| return false; |
| } |
| } |
| theme_info->theme_colors_.reset(colors_value->DeepCopy()); |
| } |
| return true; |
| } |
| |
| bool LoadTints(const base::DictionaryValue* theme_value, |
| base::string16* error, |
| ThemeInfo* theme_info) { |
| const base::DictionaryValue* tints_value = NULL; |
| if (!theme_value->GetDictionary(keys::kThemeTints, &tints_value)) |
| return true; |
| |
| // Validate that the tints are all reals. |
| for (base::DictionaryValue::Iterator iter(*tints_value); !iter.IsAtEnd(); |
| iter.Advance()) { |
| const base::ListValue* tint_list = NULL; |
| double v = 0.0; |
| if (!iter.value().GetAsList(&tint_list) || |
| tint_list->GetSize() != 3 || |
| !tint_list->GetDouble(0, &v) || |
| !tint_list->GetDouble(1, &v) || |
| !tint_list->GetDouble(2, &v)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidThemeTints); |
| return false; |
| } |
| } |
| theme_info->theme_tints_.reset(tints_value->DeepCopy()); |
| return true; |
| } |
| |
| bool LoadDisplayProperties(const base::DictionaryValue* theme_value, |
| base::string16* error, |
| ThemeInfo* theme_info) { |
| const base::DictionaryValue* display_properties_value = NULL; |
| if (theme_value->GetDictionary(keys::kThemeDisplayProperties, |
| &display_properties_value)) { |
| theme_info->theme_display_properties_.reset( |
| display_properties_value->DeepCopy()); |
| } |
| return true; |
| } |
| |
| const ThemeInfo* GetInfo(const Extension* extension) { |
| return static_cast<ThemeInfo*>(extension->GetManifestData(keys::kTheme)); |
| } |
| |
| } // namespace |
| |
| ThemeInfo::ThemeInfo() { |
| } |
| |
| ThemeInfo::~ThemeInfo() { |
| } |
| |
| // static |
| const base::DictionaryValue* ThemeInfo::GetImages(const Extension* extension) { |
| const ThemeInfo* theme_info = GetInfo(extension); |
| return theme_info ? theme_info->theme_images_.get() : NULL; |
| } |
| |
| // static |
| const base::DictionaryValue* ThemeInfo::GetColors(const Extension* extension) { |
| const ThemeInfo* theme_info = GetInfo(extension); |
| return theme_info ? theme_info->theme_colors_.get() : NULL; |
| } |
| |
| // static |
| const base::DictionaryValue* ThemeInfo::GetTints(const Extension* extension) { |
| const ThemeInfo* theme_info = GetInfo(extension); |
| return theme_info ? theme_info->theme_tints_.get() : NULL; |
| } |
| |
| // static |
| const base::DictionaryValue* ThemeInfo::GetDisplayProperties( |
| const Extension* extension) { |
| const ThemeInfo* theme_info = GetInfo(extension); |
| return theme_info ? theme_info->theme_display_properties_.get() : NULL; |
| } |
| |
| ThemeHandler::ThemeHandler() { |
| } |
| |
| ThemeHandler::~ThemeHandler() { |
| } |
| |
| bool ThemeHandler::Parse(Extension* extension, base::string16* error) { |
| const base::DictionaryValue* theme_value = NULL; |
| if (!extension->manifest()->GetDictionary(keys::kTheme, &theme_value)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidTheme); |
| return false; |
| } |
| |
| scoped_ptr<ThemeInfo> theme_info(new ThemeInfo); |
| if (!LoadImages(theme_value, error, theme_info.get())) |
| return false; |
| if (!LoadColors(theme_value, error, theme_info.get())) |
| return false; |
| if (!LoadTints(theme_value, error, theme_info.get())) |
| return false; |
| if (!LoadDisplayProperties(theme_value, error, theme_info.get())) |
| return false; |
| |
| extension->SetManifestData(keys::kTheme, theme_info.release()); |
| return true; |
| } |
| |
| bool ThemeHandler::Validate(const Extension* extension, |
| std::string* error, |
| std::vector<InstallWarning>* warnings) const { |
| // Validate that theme images exist. |
| if (extension->is_theme()) { |
| const base::DictionaryValue* images_value = |
| extensions::ThemeInfo::GetImages(extension); |
| if (images_value) { |
| for (base::DictionaryValue::Iterator iter(*images_value); !iter.IsAtEnd(); |
| iter.Advance()) { |
| std::string val; |
| if (iter.value().GetAsString(&val)) { |
| base::FilePath image_path = extension->path().Append( |
| base::FilePath::FromUTF8Unsafe(val)); |
| if (!base::PathExists(image_path)) { |
| *error = |
| l10n_util::GetStringFUTF8(IDS_EXTENSION_INVALID_IMAGE_PATH, |
| image_path.LossyDisplayName()); |
| return false; |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| const std::vector<std::string> ThemeHandler::Keys() const { |
| return SingleKey(keys::kTheme); |
| } |
| |
| } // namespace extensions |