Make default apps cache multiprofile friendly
[chromium-blink-merge.git] / chrome / browser / extensions / external_pref_loader.cc
blob567fdb25001cfde251ec07209913ffde10f3a4a8
1 // Copyright (c) 2012 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/extensions/external_pref_loader.h"
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_path.h"
11 #include "base/json/json_file_value_serializer.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "content/public/browser/browser_thread.h"
21 using content::BrowserThread;
23 namespace {
25 base::FilePath::CharType kExternalExtensionJson[] =
26 FILE_PATH_LITERAL("external_extensions.json");
28 std::set<base::FilePath> GetPrefsCandidateFilesFromFolder(
29 const base::FilePath& external_extension_search_path) {
30 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
32 std::set<base::FilePath> external_extension_paths;
34 if (!base::PathExists(external_extension_search_path)) {
35 // Does not have to exist.
36 return external_extension_paths;
39 base::FileEnumerator json_files(
40 external_extension_search_path,
41 false, // Recursive.
42 base::FileEnumerator::FILES);
43 #if defined(OS_WIN)
44 base::FilePath::StringType extension = UTF8ToWide(std::string(".json"));
45 #elif defined(OS_POSIX)
46 base::FilePath::StringType extension(".json");
47 #endif
48 do {
49 base::FilePath file = json_files.Next();
50 if (file.BaseName().value() == kExternalExtensionJson)
51 continue; // Already taken care of elsewhere.
52 if (file.empty())
53 break;
54 if (file.MatchesExtension(extension)) {
55 external_extension_paths.insert(file.BaseName());
56 } else {
57 DVLOG(1) << "Not considering: " << file.LossyDisplayName()
58 << " (does not have a .json extension)";
60 } while (true);
62 return external_extension_paths;
65 // Extracts extension information from a json file serialized by |serializer|.
66 // |path| is only used for informational purposes (outputted when an error
67 // occurs). An empty dictionary is returned in case of failure (e.g. invalid
68 // path or json content).
69 // Caller takes ownership of the returned dictionary.
70 DictionaryValue* ExtractExtensionPrefs(base::ValueSerializer* serializer,
71 const base::FilePath& path) {
72 std::string error_msg;
73 Value* extensions = serializer->Deserialize(NULL, &error_msg);
74 if (!extensions) {
75 LOG(WARNING) << "Unable to deserialize json data: " << error_msg
76 << " in file " << path.value() << ".";
77 return new DictionaryValue;
80 DictionaryValue* ext_dictionary = NULL;
81 if (extensions->GetAsDictionary(&ext_dictionary))
82 return ext_dictionary;
84 LOG(WARNING) << "Expected a JSON dictionary in file "
85 << path.value() << ".";
86 return new DictionaryValue;
89 } // namespace
91 namespace extensions {
93 ExternalPrefLoader::ExternalPrefLoader(int base_path_id, Options options)
94 : base_path_id_(base_path_id), options_(options) {
95 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98 const base::FilePath ExternalPrefLoader::GetBaseCrxFilePath() {
99 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
101 // |base_path_| was set in LoadOnFileThread().
102 return base_path_;
105 void ExternalPrefLoader::StartLoading() {
106 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107 BrowserThread::PostTask(
108 BrowserThread::FILE, FROM_HERE,
109 base::Bind(&ExternalPrefLoader::LoadOnFileThread, this));
112 void ExternalPrefLoader::LoadOnFileThread() {
113 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
115 scoped_ptr<DictionaryValue> prefs(new DictionaryValue);
117 // TODO(skerner): Some values of base_path_id_ will cause
118 // PathService::Get() to return false, because the path does
119 // not exist. Find and fix the build/install scripts so that
120 // this can become a CHECK(). Known examples include chrome
121 // OS developer builds and linux install packages.
122 // Tracked as crbug.com/70402 .
123 if (PathService::Get(base_path_id_, &base_path_)) {
124 ReadExternalExtensionPrefFile(prefs.get());
126 if (!prefs->empty())
127 LOG(WARNING) << "You are using an old-style extension deployment method "
128 "(external_extensions.json), which will soon be "
129 "deprecated. (see http://code.google.com/chrome/"
130 "extensions/external_extensions.html )";
132 ReadStandaloneExtensionPrefFiles(prefs.get());
135 prefs_.swap(prefs);
137 if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
138 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
139 prefs_->size());
142 // If we have any records to process, then we must have
143 // read at least one .json file. If so, then we should have
144 // set |base_path_|.
145 if (!prefs_->empty())
146 CHECK(!base_path_.empty());
148 BrowserThread::PostTask(
149 BrowserThread::UI, FROM_HERE,
150 base::Bind(&ExternalPrefLoader::LoadFinished, this));
153 void ExternalPrefLoader::ReadExternalExtensionPrefFile(DictionaryValue* prefs) {
154 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
155 CHECK(NULL != prefs);
157 base::FilePath json_file = base_path_.Append(kExternalExtensionJson);
159 if (!base::PathExists(json_file)) {
160 // This is not an error. The file does not exist by default.
161 return;
164 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) {
165 #if defined(OS_MACOSX)
166 if (!file_util::VerifyPathControlledByAdmin(json_file)) {
167 LOG(ERROR) << "Can not read external extensions source. The file "
168 << json_file.value() << " and every directory in its path, "
169 << "must be owned by root, have group \"admin\", and not be "
170 << "writable by all users. These restrictions prevent "
171 << "unprivleged users from making chrome install extensions "
172 << "on other users' accounts.";
173 return;
175 #else
176 // The only platform that uses this check is Mac OS. If you add one,
177 // you need to implement file_util::VerifyPathControlledByAdmin() for
178 // that platform.
179 NOTREACHED();
180 #endif // defined(OS_MACOSX)
183 JSONFileValueSerializer serializer(json_file);
184 scoped_ptr<DictionaryValue> ext_prefs(
185 ExtractExtensionPrefs(&serializer, json_file));
186 if (ext_prefs)
187 prefs->MergeDictionary(ext_prefs.get());
190 void ExternalPrefLoader::ReadStandaloneExtensionPrefFiles(
191 DictionaryValue* prefs) {
192 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
193 CHECK(NULL != prefs);
195 // First list the potential .json candidates.
196 std::set<base::FilePath>
197 candidates = GetPrefsCandidateFilesFromFolder(base_path_);
198 if (candidates.empty()) {
199 DVLOG(1) << "Extension candidates list empty";
200 return;
203 // For each file read the json description & build the proper
204 // associated prefs.
205 for (std::set<base::FilePath>::const_iterator it = candidates.begin();
206 it != candidates.end();
207 ++it) {
208 base::FilePath extension_candidate_path = base_path_.Append(*it);
210 std::string id =
211 #if defined(OS_WIN)
212 WideToASCII(
213 extension_candidate_path.RemoveExtension().BaseName().value());
214 #elif defined(OS_POSIX)
215 extension_candidate_path.RemoveExtension().BaseName().value().c_str();
216 #endif
218 DVLOG(1) << "Reading json file: "
219 << extension_candidate_path.LossyDisplayName().c_str();
221 JSONFileValueSerializer serializer(extension_candidate_path);
222 scoped_ptr<DictionaryValue> ext_prefs(
223 ExtractExtensionPrefs(&serializer, extension_candidate_path));
224 if (ext_prefs) {
225 DVLOG(1) << "Adding extension with id: " << id;
226 prefs->Set(id, ext_prefs.release());
231 ExternalTestingLoader::ExternalTestingLoader(
232 const std::string& json_data,
233 const base::FilePath& fake_base_path)
234 : fake_base_path_(fake_base_path) {
235 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236 JSONStringValueSerializer serializer(json_data);
237 base::FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
238 testing_prefs_.reset(ExtractExtensionPrefs(&serializer, fake_json_path));
241 void ExternalTestingLoader::StartLoading() {
242 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243 prefs_.reset(testing_prefs_->DeepCopy());
244 LoadFinished();
247 ExternalTestingLoader::~ExternalTestingLoader() {}
249 const base::FilePath ExternalTestingLoader::GetBaseCrxFilePath() {
250 return fake_base_path_;
253 } // extensions