1 // Copyright (c) 2013 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/manifest_handlers/theme_handler.h" 6 7 #include "base/file_util.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "base/values.h" 11 #include "chrome/common/extensions/extension_manifest_constants.h" 12 #include "chrome/common/extensions/manifest.h" 13 #include "grit/generated_resources.h" 14 #include "ui/base/l10n/l10n_util.h" 15 16 namespace extensions { 17 18 namespace keys = extension_manifest_keys; 19 namespace errors = extension_manifest_errors; 20 21 namespace { 22 23 bool LoadImages(const base::DictionaryValue* theme_value, 24 string16* error, 25 ThemeInfo* theme_info) { 26 const base::DictionaryValue* images_value = NULL; 27 if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { 28 // Validate that the images are all strings. 29 for (base::DictionaryValue::Iterator iter(*images_value); !iter.IsAtEnd(); 30 iter.Advance()) { 31 // The value may be a dictionary of scales and files paths. 32 // Or the value may be a file path, in which case a scale 33 // of 100% is assumed. 34 if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) { 35 const base::DictionaryValue* inner_value = NULL; 36 if (iter.value().GetAsDictionary(&inner_value)) { 37 for (base::DictionaryValue::Iterator inner_iter(*inner_value); 38 !inner_iter.IsAtEnd(); inner_iter.Advance()) { 39 if (!inner_iter.value().IsType(base::Value::TYPE_STRING)) { 40 *error = ASCIIToUTF16(errors::kInvalidThemeImages); 41 return false; 42 } 43 } 44 } else { 45 *error = ASCIIToUTF16(errors::kInvalidThemeImages); 46 return false; 47 } 48 } else if (!iter.value().IsType(base::Value::TYPE_STRING)) { 49 *error = ASCIIToUTF16(errors::kInvalidThemeImages); 50 return false; 51 } 52 } 53 theme_info->theme_images_.reset(images_value->DeepCopy()); 54 } 55 return true; 56 } 57 58 bool LoadColors(const base::DictionaryValue* theme_value, 59 string16* error, 60 ThemeInfo* theme_info) { 61 const base::DictionaryValue* colors_value = NULL; 62 if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { 63 // Validate that the colors are RGB or RGBA lists. 64 for (base::DictionaryValue::Iterator iter(*colors_value); !iter.IsAtEnd(); 65 iter.Advance()) { 66 const base::ListValue* color_list = NULL; 67 double alpha = 0.0; 68 int color = 0; 69 // The color must be a list... 70 if (!iter.value().GetAsList(&color_list) || 71 // ... and either 3 items (RGB) or 4 (RGBA). 72 ((color_list->GetSize() != 3) && 73 ((color_list->GetSize() != 4) || 74 // For RGBA, the fourth item must be a real or int alpha value. 75 // Note that GetDouble() can get an integer value. 76 !color_list->GetDouble(3, &alpha))) || 77 // For both RGB and RGBA, the first three items must be ints (R,G,B). 78 !color_list->GetInteger(0, &color) || 79 !color_list->GetInteger(1, &color) || 80 !color_list->GetInteger(2, &color)) { 81 *error = ASCIIToUTF16(errors::kInvalidThemeColors); 82 return false; 83 } 84 } 85 theme_info->theme_colors_.reset(colors_value->DeepCopy()); 86 } 87 return true; 88 } 89 90 bool LoadTints(const base::DictionaryValue* theme_value, 91 string16* error, 92 ThemeInfo* theme_info) { 93 const base::DictionaryValue* tints_value = NULL; 94 if (!theme_value->GetDictionary(keys::kThemeTints, &tints_value)) 95 return true; 96 97 // Validate that the tints are all reals. 98 for (base::DictionaryValue::Iterator iter(*tints_value); !iter.IsAtEnd(); 99 iter.Advance()) { 100 const base::ListValue* tint_list = NULL; 101 double v = 0.0; 102 if (!iter.value().GetAsList(&tint_list) || 103 tint_list->GetSize() != 3 || 104 !tint_list->GetDouble(0, &v) || 105 !tint_list->GetDouble(1, &v) || 106 !tint_list->GetDouble(2, &v)) { 107 *error = ASCIIToUTF16(errors::kInvalidThemeTints); 108 return false; 109 } 110 } 111 theme_info->theme_tints_.reset(tints_value->DeepCopy()); 112 return true; 113 } 114 115 bool LoadDisplayProperties(const base::DictionaryValue* theme_value, 116 string16* error, 117 ThemeInfo* theme_info) { 118 const base::DictionaryValue* display_properties_value = NULL; 119 if (theme_value->GetDictionary(keys::kThemeDisplayProperties, 120 &display_properties_value)) { 121 theme_info->theme_display_properties_.reset( 122 display_properties_value->DeepCopy()); 123 } 124 return true; 125 } 126 127 const ThemeInfo* GetInfo(const Extension* extension) { 128 return static_cast<ThemeInfo*>(extension->GetManifestData(keys::kTheme)); 129 } 130 131 } // namespace 132 133 ThemeInfo::ThemeInfo() { 134 } 135 136 ThemeInfo::~ThemeInfo() { 137 } 138 139 // static 140 const base::DictionaryValue* ThemeInfo::GetImages(const Extension* extension) { 141 const ThemeInfo* theme_info = GetInfo(extension); 142 return theme_info ? theme_info->theme_images_.get() : NULL; 143 } 144 145 // static 146 const base::DictionaryValue* ThemeInfo::GetColors(const Extension* extension) { 147 const ThemeInfo* theme_info = GetInfo(extension); 148 return theme_info ? theme_info->theme_colors_.get() : NULL; 149 } 150 151 // static 152 const base::DictionaryValue* ThemeInfo::GetTints(const Extension* extension) { 153 const ThemeInfo* theme_info = GetInfo(extension); 154 return theme_info ? theme_info->theme_tints_.get() : NULL; 155 } 156 157 // static 158 const base::DictionaryValue* ThemeInfo::GetDisplayProperties( 159 const Extension* extension) { 160 const ThemeInfo* theme_info = GetInfo(extension); 161 return theme_info ? theme_info->theme_display_properties_.get() : NULL; 162 } 163 164 ThemeHandler::ThemeHandler() { 165 } 166 167 ThemeHandler::~ThemeHandler() { 168 } 169 170 bool ThemeHandler::Parse(Extension* extension, string16* error) { 171 const base::DictionaryValue* theme_value = NULL; 172 if (!extension->manifest()->GetDictionary(keys::kTheme, &theme_value)) { 173 *error = ASCIIToUTF16(errors::kInvalidTheme); 174 return false; 175 } 176 177 scoped_ptr<ThemeInfo> theme_info(new ThemeInfo); 178 if (!LoadImages(theme_value, error, theme_info.get())) 179 return false; 180 if (!LoadColors(theme_value, error, theme_info.get())) 181 return false; 182 if (!LoadTints(theme_value, error, theme_info.get())) 183 return false; 184 if (!LoadDisplayProperties(theme_value, error, theme_info.get())) 185 return false; 186 187 extension->SetManifestData(keys::kTheme, theme_info.release()); 188 return true; 189 } 190 191 bool ThemeHandler::Validate(const Extension* extension, 192 std::string* error, 193 std::vector<InstallWarning>* warnings) const { 194 // Validate that theme images exist. 195 if (extension->is_theme()) { 196 const base::DictionaryValue* images_value = 197 extensions::ThemeInfo::GetImages(extension); 198 if (images_value) { 199 for (base::DictionaryValue::Iterator iter(*images_value); !iter.IsAtEnd(); 200 iter.Advance()) { 201 std::string val; 202 if (iter.value().GetAsString(&val)) { 203 base::FilePath image_path = extension->path().Append( 204 base::FilePath::FromUTF8Unsafe(val)); 205 if (!base::PathExists(image_path)) { 206 *error = 207 l10n_util::GetStringFUTF8(IDS_EXTENSION_INVALID_IMAGE_PATH, 208 image_path.LossyDisplayName()); 209 return false; 210 } 211 } 212 } 213 } 214 } 215 return true; 216 } 217 218 const std::vector<std::string> ThemeHandler::Keys() const { 219 return SingleKey(keys::kTheme); 220 } 221 222 } // namespace extensions 223