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/files/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/grit/generated_resources.h"
12#include "extensions/common/manifest.h"
13#include "extensions/common/manifest_constants.h"
14#include "ui/base/l10n/l10n_util.h"
15
16namespace extensions {
17
18namespace keys = manifest_keys;
19namespace errors = manifest_errors;
20
21namespace {
22
23bool LoadImages(const base::DictionaryValue* theme_value,
24                base::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 = base::ASCIIToUTF16(errors::kInvalidThemeImages);
41              return false;
42            }
43          }
44        } else {
45          *error = base::ASCIIToUTF16(errors::kInvalidThemeImages);
46          return false;
47        }
48      } else if (!iter.value().IsType(base::Value::TYPE_STRING)) {
49        *error = base::ASCIIToUTF16(errors::kInvalidThemeImages);
50        return false;
51      }
52    }
53    theme_info->theme_images_.reset(images_value->DeepCopy());
54  }
55  return true;
56}
57
58bool LoadColors(const base::DictionaryValue* theme_value,
59                base::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 = base::ASCIIToUTF16(errors::kInvalidThemeColors);
82        return false;
83      }
84    }
85    theme_info->theme_colors_.reset(colors_value->DeepCopy());
86  }
87  return true;
88}
89
90bool LoadTints(const base::DictionaryValue* theme_value,
91               base::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 = base::ASCIIToUTF16(errors::kInvalidThemeTints);
108      return false;
109    }
110  }
111  theme_info->theme_tints_.reset(tints_value->DeepCopy());
112  return true;
113}
114
115bool LoadDisplayProperties(const base::DictionaryValue* theme_value,
116                           base::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
127const ThemeInfo* GetInfo(const Extension* extension) {
128  return static_cast<ThemeInfo*>(extension->GetManifestData(keys::kTheme));
129}
130
131}  // namespace
132
133ThemeInfo::ThemeInfo() {
134}
135
136ThemeInfo::~ThemeInfo() {
137}
138
139// static
140const 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
146const 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
152const 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
158const 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
164ThemeHandler::ThemeHandler() {
165}
166
167ThemeHandler::~ThemeHandler() {
168}
169
170bool ThemeHandler::Parse(Extension* extension, base::string16* error) {
171  const base::DictionaryValue* theme_value = NULL;
172  if (!extension->manifest()->GetDictionary(keys::kTheme, &theme_value)) {
173    *error = base::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
191bool 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
218const std::vector<std::string> ThemeHandler::Keys() const {
219  return SingleKey(keys::kTheme);
220}
221
222}  // namespace extensions
223