Home | History | Annotate | Download | only in manifest_handlers
      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