Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / supervised_user / supervised_user_site_list.cc
blob9b78e64252714a5732f72ebafd9f2b4de00fdbc8
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/files/file_util.h"
8 #include "base/json/json_file_value_serializer.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/task_runner_util.h"
12 #include "base/values.h"
13 #include "components/safe_json/safe_json_parser.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "url/gurl.h"
17 const int kSitelistFormatVersion = 1;
19 const char kHostnameHashesKey[] = "hostname_hashes";
20 const char kNameKey[] = "name";
21 const char kSitesKey[] = "sites";
22 const char kSitelistFormatVersionKey[] = "version";
23 const char kUrlKey[] = "url";
24 const char kWhitelistKey[] = "whitelist";
26 namespace {
28 std::string ReadFileOnBlockingThread(const base::FilePath& path) {
29 SCOPED_UMA_HISTOGRAM_TIMER("ManagedUsers.Whitelist.ReadDuration");
30 std::string contents;
31 base::ReadFileToString(path, &contents);
32 return contents;
35 void HandleError(const base::FilePath& path, const std::string& error) {
36 LOG(ERROR) << "Couldn't load site list " << path.value() << ": " << error;
39 // Takes a DictionaryValue entry from the JSON file and fills the whitelist
40 // (via URL patterns or hostname hashes) and the URL in the corresponding Site
41 // struct.
42 void AddWhitelistEntries(const base::DictionaryValue* site_dict,
43 SupervisedUserSiteList::Site* site) {
44 bool found = false;
46 const base::ListValue* whitelist = nullptr;
47 if (site_dict->GetList(kWhitelistKey, &whitelist)) {
48 found = true;
49 for (const base::Value* entry : *whitelist) {
50 std::string pattern;
51 if (!entry->GetAsString(&pattern)) {
52 LOG(ERROR) << "Invalid whitelist entry";
53 continue;
56 site->patterns.push_back(pattern);
60 const base::ListValue* hash_list = nullptr;
61 if (site_dict->GetList(kHostnameHashesKey, &hash_list)) {
62 found = true;
63 for (const base::Value* entry : *hash_list) {
64 std::string hash;
65 if (!entry->GetAsString(&hash)) {
66 LOG(ERROR) << "Invalid hostname_hashes entry";
67 continue;
69 // TODO(treib): Check that |hash| has exactly 40 (2*base::kSHA1Length)
70 // characters from [0-9a-fA-F]. Or just store the raw bytes (from
71 // base::HexStringToBytes).
73 site->hostname_hashes.push_back(hash);
77 if (found)
78 return;
80 // Fall back to using a whitelist based on the URL.
81 std::string url_str;
82 if (!site_dict->GetString(kUrlKey, &url_str)) {
83 LOG(ERROR) << "Whitelist is invalid";
84 return;
87 GURL url(url_str);
88 if (!url.is_valid()) {
89 LOG(ERROR) << "URL " << url_str << " is invalid";
90 return;
93 site->patterns.push_back(url.host());
96 } // namespace
98 SupervisedUserSiteList::Site::Site(const base::string16& name) : name(name) {
101 SupervisedUserSiteList::Site::~Site() {
104 void SupervisedUserSiteList::Load(const base::FilePath& path,
105 const LoadedCallback& callback) {
106 base::PostTaskAndReplyWithResult(
107 content::BrowserThread::GetBlockingPool()
108 ->GetTaskRunnerWithShutdownBehavior(
109 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN).get(),
110 FROM_HERE,
111 base::Bind(&ReadFileOnBlockingThread, path),
112 base::Bind(&SupervisedUserSiteList::ParseJson, path, callback));
115 SupervisedUserSiteList::SupervisedUserSiteList(const base::ListValue& sites) {
116 for (const base::Value* site : sites) {
117 const base::DictionaryValue* entry = nullptr;
118 if (!site->GetAsDictionary(&entry)) {
119 LOG(ERROR) << "Entry is invalid";
120 continue;
123 base::string16 name;
124 entry->GetString(kNameKey, &name);
125 sites_.push_back(Site(name));
126 AddWhitelistEntries(entry, &sites_.back());
130 SupervisedUserSiteList::~SupervisedUserSiteList() {
133 // static
134 void SupervisedUserSiteList::ParseJson(
135 const base::FilePath& path,
136 const SupervisedUserSiteList::LoadedCallback& callback,
137 const std::string& json) {
138 // TODO(bauerb): Use JSONSanitizer to sanitize whitelists on installation
139 // instead of using the expensive SafeJsonParser on every load.
140 safe_json::SafeJsonParser::Parse(
141 json, base::Bind(&SupervisedUserSiteList::OnJsonParseSucceeded, path,
142 base::TimeTicks::Now(), callback),
143 base::Bind(&HandleError, path));
146 // static
147 void SupervisedUserSiteList::OnJsonParseSucceeded(
148 const base::FilePath& path,
149 base::TimeTicks start_time,
150 const SupervisedUserSiteList::LoadedCallback& callback,
151 scoped_ptr<base::Value> value) {
152 if (!start_time.is_null()) {
153 UMA_HISTOGRAM_TIMES("ManagedUsers.Whitelist.JsonParseDuration",
154 base::TimeTicks::Now() - start_time);
157 base::DictionaryValue* dict = nullptr;
158 if (!value->GetAsDictionary(&dict)) {
159 LOG(ERROR) << "Site list " << path.value() << " is invalid";
160 return;
163 int version = 0;
164 if (!dict->GetInteger(kSitelistFormatVersionKey, &version)) {
165 LOG(ERROR) << "Site list " << path.value() << " has invalid version";
166 return;
169 if (version > kSitelistFormatVersion) {
170 LOG(ERROR) << "Site list " << path.value() << " has a too new format";
171 return;
174 base::ListValue* sites = nullptr;
175 if (!dict->GetList(kSitesKey, &sites)) {
176 LOG(ERROR) << "Site list " << path.value() << " does not contain any sites";
177 return;
180 callback.Run(make_scoped_refptr(new SupervisedUserSiteList(*sites)));