1 // Copyright 2014 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.
5 #include "chrome/browser/supervised_user/supervised_user_site_list.h"
7 #include "base/json/json_file_value_serializer.h"
8 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "extensions/common/extension.h"
13 const int kSitelistFormatVersion
= 1;
15 const char kCategoriesKey
[] = "categories";
16 const char kHostnameHashesKey
[] = "hostname_hashes";
17 const char kNameKey
[] = "name";
18 const char kSitesKey
[] = "sites";
19 const char kSitelistFormatVersionKey
[] = "version";
20 const char kThumbnailKey
[] = "thumbnail";
21 const char kThumbnailUrlKey
[] = "thumbnail_url";
22 const char kUrlKey
[] = "url";
23 const char kWhitelistKey
[] = "whitelist";
28 const char* identifier
;
32 // These are placeholders for now.
33 CategoryInfo g_categories
[] = {
34 {"com.google.chrome.animals", "Animals and Plants"},
35 {"com.google.chrome.arts", "Arts"},
36 {"com.google.chrome.business", "Business"},
37 {"com.google.chrome.computers", "Computers"},
38 {"com.google.chrome.education", "Education"},
39 {"com.google.chrome.entertainment", "Entertainment"},
40 {"com.google.chrome.games", "Games"},
41 {"com.google.chrome.health", "Health"},
42 {"com.google.chrome.home", "Home"},
43 {"com.google.chrome.international", "International"},
44 {"com.google.chrome.news", "News"},
45 {"com.google.chrome.people", "People and Society"},
46 {"com.google.chrome.places", "Places"},
47 {"com.google.chrome.pre-school", "Pre-School"},
48 {"com.google.chrome.reference", "Reference"},
49 {"com.google.chrome.science", "Science"},
50 {"com.google.chrome.shopping", "Shopping"},
51 {"com.google.chrome.sports", "Sports and Hobbies"},
52 {"com.google.chrome.teens", "Teens"}
55 // Category 0 is "not listed"; actual category IDs start at 1.
56 int GetCategoryId(const std::string
& category
) {
57 for (size_t i
= 0; i
< arraysize(g_categories
); ++i
) {
58 if (g_categories
[i
].identifier
== category
)
64 // Takes a DictionaryValue entry from the JSON file and fills the whitelist
65 // (via URL patterns or hostname hashes) and the URL in the corresponding Site
67 void AddWhitelistEntries(const base::DictionaryValue
* site_dict
,
68 SupervisedUserSiteList::Site
* site
) {
69 std::vector
<std::string
>* patterns
= &site
->patterns
;
72 const base::ListValue
* whitelist
= NULL
;
73 if (site_dict
->GetList(kWhitelistKey
, &whitelist
)) {
75 for (const base::Value
* entry
: *whitelist
) {
77 if (!entry
->GetAsString(&pattern
)) {
78 LOG(ERROR
) << "Invalid whitelist entry";
82 patterns
->push_back(pattern
);
86 std::vector
<std::string
>* hashes
= &site
->hostname_hashes
;
87 const base::ListValue
* hash_list
= NULL
;
88 if (site_dict
->GetList(kHostnameHashesKey
, &hash_list
)) {
90 for (const base::Value
* entry
: *hash_list
) {
92 if (!entry
->GetAsString(&hash
)) {
93 LOG(ERROR
) << "Invalid whitelist entry";
97 hashes
->push_back(hash
);
104 // Fall back to using a whitelist based on the URL.
106 if (!site_dict
->GetString(kUrlKey
, &url_str
)) {
107 LOG(ERROR
) << "Whitelist is invalid";
112 if (!url
.is_valid()) {
113 LOG(ERROR
) << "URL " << url_str
<< " is invalid";
117 patterns
->push_back(url
.host());
122 SupervisedUserSiteList::Site::Site(const base::string16
& name
,
125 category_id(category_id
) {}
127 SupervisedUserSiteList::Site::~Site() {}
129 SupervisedUserSiteList::SupervisedUserSiteList(
130 const std::string
& extension_id
,
131 const base::FilePath
& path
)
132 : extension_id_(extension_id
),
136 SupervisedUserSiteList::~SupervisedUserSiteList() {
139 SupervisedUserSiteList
* SupervisedUserSiteList::Clone() const {
140 return new SupervisedUserSiteList(extension_id_
, path_
);
144 void SupervisedUserSiteList::GetCategoryNames(
145 std::vector
<base::string16
>* categories
) {
146 // TODO(bauerb): Collect custom categories from extensions.
147 for (size_t i
= 0; i
< arraysize(g_categories
); ++i
) {
148 categories
->push_back(base::ASCIIToUTF16(g_categories
[i
].name
));
152 void SupervisedUserSiteList::GetSites(std::vector
<Site
>* sites
) {
156 for (const base::Value
* site
: *sites_
) {
157 const base::DictionaryValue
* entry
= NULL
;
158 if (!site
->GetAsDictionary(&entry
)) {
159 LOG(ERROR
) << "Entry is invalid";
164 entry
->GetString(kNameKey
, &name
);
166 // TODO(bauerb): We need to distinguish between "no category assigned" and
167 // "not on any site list".
169 const base::ListValue
* categories
= NULL
;
170 if (entry
->GetList(kCategoriesKey
, &categories
)) {
171 for (const base::Value
* category_entry
: *categories
) {
172 std::string category
;
173 if (!category_entry
->GetAsString(&category
)) {
174 LOG(ERROR
) << "Invalid category";
177 category_id
= GetCategoryId(category
);
181 sites
->push_back(Site(name
, category_id
));
182 AddWhitelistEntries(entry
, &sites
->back());
186 bool SupervisedUserSiteList::LazyLoad() {
190 JSONFileValueSerializer
serializer(path_
);
192 scoped_ptr
<base::Value
> value(serializer
.Deserialize(NULL
, &error
));
194 LOG(ERROR
) << "Couldn't load site list " << path_
.value() << ": "
199 base::DictionaryValue
* dict
= NULL
;
200 if (!value
->GetAsDictionary(&dict
)) {
201 LOG(ERROR
) << "Site list " << path_
.value() << " is invalid";
206 if (!dict
->GetInteger(kSitelistFormatVersionKey
, &version
)) {
207 LOG(ERROR
) << "Site list " << path_
.value() << " has invalid version";
211 if (version
> kSitelistFormatVersion
) {
212 LOG(ERROR
) << "Site list " << path_
.value() << " has a too new format";
216 base::ListValue
* sites
= NULL
;
217 if (dict
->GetList(kSitesKey
, &sites
))
218 sites_
.reset(sites
->DeepCopy());
220 base::DictionaryValue
* categories
= NULL
;
221 if (dict
->GetDictionary(kCategoriesKey
, &categories
))
222 categories_
.reset(categories
->DeepCopy());
227 void SupervisedUserSiteList::CopyThumbnailUrl(
228 const base::DictionaryValue
* source
,
229 base::DictionaryValue
* dest
) {
230 if (!source
->HasKey(kThumbnailKey
))
233 std::string thumbnail
;
234 if (!source
->GetString(kThumbnailKey
, &thumbnail
)) {
235 LOG(ERROR
) << "Invalid thumbnail";
240 extensions::Extension::GetBaseURLFromExtensionId(extension_id_
);
241 GURL thumbnail_url
= base_url
.Resolve(thumbnail
);
242 if (!thumbnail_url
.is_valid()) {
243 LOG(ERROR
) << "Invalid thumbnail";
247 dest
->SetString(kThumbnailUrlKey
, thumbnail_url
.spec());