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