Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / extensions / external_pref_loader.cc
blobc0775d2de2c070883bf455ea2cf1fdded82d4279
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/files/file_enumerator.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.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/browser/defaults.h"
19 #include "chrome/browser/prefs/pref_service_syncable_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sync/profile_sync_service_factory.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "components/syncable_prefs/pref_service_syncable.h"
24 #include "content/public/browser/browser_thread.h"
26 using content::BrowserThread;
28 namespace {
30 base::FilePath::CharType kExternalExtensionJson[] =
31 FILE_PATH_LITERAL("external_extensions.json");
33 std::set<base::FilePath> GetPrefsCandidateFilesFromFolder(
34 const base::FilePath& external_extension_search_path) {
35 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
37 std::set<base::FilePath> external_extension_paths;
39 if (!base::PathExists(external_extension_search_path)) {
40 // Does not have to exist.
41 return external_extension_paths;
44 base::FileEnumerator json_files(
45 external_extension_search_path,
46 false, // Recursive.
47 base::FileEnumerator::FILES);
48 #if defined(OS_WIN)
49 base::FilePath::StringType extension = base::UTF8ToWide(".json");
50 #elif defined(OS_POSIX)
51 base::FilePath::StringType extension(".json");
52 #endif
53 do {
54 base::FilePath file = json_files.Next();
55 if (file.BaseName().value() == kExternalExtensionJson)
56 continue; // Already taken care of elsewhere.
57 if (file.empty())
58 break;
59 if (file.MatchesExtension(extension)) {
60 external_extension_paths.insert(file.BaseName());
61 } else {
62 DVLOG(1) << "Not considering: " << file.LossyDisplayName()
63 << " (does not have a .json extension)";
65 } while (true);
67 return external_extension_paths;
70 // Extracts extension information from a json file serialized by |serializer|.
71 // |path| is only used for informational purposes (outputted when an error
72 // occurs). An empty dictionary is returned in case of failure (e.g. invalid
73 // path or json content).
74 // Caller takes ownership of the returned dictionary.
75 base::DictionaryValue* ExtractExtensionPrefs(
76 base::ValueDeserializer* deserializer,
77 const base::FilePath& path) {
78 std::string error_msg;
79 base::Value* extensions = deserializer->Deserialize(NULL, &error_msg);
80 if (!extensions) {
81 LOG(WARNING) << "Unable to deserialize json data: " << error_msg
82 << " in file " << path.value() << ".";
83 return new base::DictionaryValue;
86 base::DictionaryValue* ext_dictionary = NULL;
87 if (extensions->GetAsDictionary(&ext_dictionary))
88 return ext_dictionary;
90 LOG(WARNING) << "Expected a JSON dictionary in file "
91 << path.value() << ".";
92 return new base::DictionaryValue;
95 } // namespace
97 namespace extensions {
99 ExternalPrefLoader::ExternalPrefLoader(int base_path_id,
100 Options options,
101 Profile* profile)
102 : base_path_id_(base_path_id),
103 options_(options),
104 profile_(profile),
105 syncable_pref_observer_(this) {
106 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
109 ExternalPrefLoader::~ExternalPrefLoader() {
112 const base::FilePath ExternalPrefLoader::GetBaseCrxFilePath() {
113 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
115 // |base_path_| was set in LoadOnFileThread().
116 return base_path_;
119 void ExternalPrefLoader::StartLoading() {
120 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
121 if ((options_ & DELAY_LOAD_UNTIL_PRIORITY_SYNC) &&
122 (profile_ && profile_->IsSyncAllowed())) {
123 if (!PostLoadIfPrioritySyncReady()) {
124 DCHECK(profile_);
125 syncable_prefs::PrefServiceSyncable* prefs =
126 PrefServiceSyncableFromProfile(profile_);
127 DCHECK(prefs);
128 syncable_pref_observer_.Add(prefs);
129 ProfileSyncService* service =
130 ProfileSyncServiceFactory::GetForProfile(profile_);
131 DCHECK(service);
132 if (service->CanSyncStart() &&
133 (service->HasSyncSetupCompleted() ||
134 browser_defaults::kSyncAutoStarts)) {
135 service->AddObserver(this);
136 } else {
137 PostLoadAndRemoveObservers();
140 } else {
141 BrowserThread::PostTask(
142 BrowserThread::FILE, FROM_HERE,
143 base::Bind(&ExternalPrefLoader::LoadOnFileThread, this));
147 void ExternalPrefLoader::OnIsSyncingChanged() {
148 PostLoadIfPrioritySyncReady();
151 void ExternalPrefLoader::OnStateChanged() {
152 ProfileSyncService* service =
153 ProfileSyncServiceFactory::GetForProfile(profile_);
154 DCHECK(service);
155 if (!service->CanSyncStart()) {
156 PostLoadAndRemoveObservers();
160 bool ExternalPrefLoader::PostLoadIfPrioritySyncReady() {
161 DCHECK(options_ & DELAY_LOAD_UNTIL_PRIORITY_SYNC);
162 DCHECK(profile_);
164 syncable_prefs::PrefServiceSyncable* prefs =
165 PrefServiceSyncableFromProfile(profile_);
166 DCHECK(prefs);
167 if (prefs->IsPrioritySyncing()) {
168 PostLoadAndRemoveObservers();
169 return true;
172 return false;
175 void ExternalPrefLoader::PostLoadAndRemoveObservers() {
176 syncable_prefs::PrefServiceSyncable* prefs =
177 PrefServiceSyncableFromProfile(profile_);
178 DCHECK(prefs);
179 syncable_pref_observer_.Remove(prefs);
181 ProfileSyncService* service =
182 ProfileSyncServiceFactory::GetForProfile(profile_);
183 DCHECK(service);
184 service->RemoveObserver(this);
186 BrowserThread::PostTask(
187 BrowserThread::FILE, FROM_HERE,
188 base::Bind(&ExternalPrefLoader::LoadOnFileThread, this));
191 void ExternalPrefLoader::LoadOnFileThread() {
192 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
194 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
196 // TODO(skerner): Some values of base_path_id_ will cause
197 // PathService::Get() to return false, because the path does
198 // not exist. Find and fix the build/install scripts so that
199 // this can become a CHECK(). Known examples include chrome
200 // OS developer builds and linux install packages.
201 // Tracked as crbug.com/70402 .
202 if (PathService::Get(base_path_id_, &base_path_)) {
203 ReadExternalExtensionPrefFile(prefs.get());
205 if (!prefs->empty())
206 LOG(WARNING) << "You are using an old-style extension deployment method "
207 "(external_extensions.json), which will soon be "
208 "deprecated. (see http://developer.chrome.com/"
209 "extensions/external_extensions.html)";
211 ReadStandaloneExtensionPrefFiles(prefs.get());
214 prefs_.swap(prefs);
216 if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
217 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
218 prefs_->size());
221 // If we have any records to process, then we must have
222 // read at least one .json file. If so, then we should have
223 // set |base_path_|.
224 if (!prefs_->empty())
225 CHECK(!base_path_.empty());
227 BrowserThread::PostTask(
228 BrowserThread::UI, FROM_HERE,
229 base::Bind(&ExternalPrefLoader::LoadFinished, this));
232 void ExternalPrefLoader::ReadExternalExtensionPrefFile(
233 base::DictionaryValue* prefs) {
234 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
235 CHECK(NULL != prefs);
237 base::FilePath json_file = base_path_.Append(kExternalExtensionJson);
239 if (!base::PathExists(json_file)) {
240 // This is not an error. The file does not exist by default.
241 return;
244 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) {
245 #if defined(OS_MACOSX)
246 if (!base::VerifyPathControlledByAdmin(json_file)) {
247 LOG(ERROR) << "Can not read external extensions source. The file "
248 << json_file.value() << " and every directory in its path, "
249 << "must be owned by root, have group \"admin\", and not be "
250 << "writable by all users. These restrictions prevent "
251 << "unprivleged users from making chrome install extensions "
252 << "on other users' accounts.";
253 return;
255 #else
256 // The only platform that uses this check is Mac OS. If you add one,
257 // you need to implement base::VerifyPathControlledByAdmin() for
258 // that platform.
259 NOTREACHED();
260 #endif // defined(OS_MACOSX)
263 JSONFileValueDeserializer deserializer(json_file);
264 scoped_ptr<base::DictionaryValue> ext_prefs(
265 ExtractExtensionPrefs(&deserializer, json_file));
266 if (ext_prefs)
267 prefs->MergeDictionary(ext_prefs.get());
270 void ExternalPrefLoader::ReadStandaloneExtensionPrefFiles(
271 base::DictionaryValue* prefs) {
272 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
273 CHECK(NULL != prefs);
275 // First list the potential .json candidates.
276 std::set<base::FilePath>
277 candidates = GetPrefsCandidateFilesFromFolder(base_path_);
278 if (candidates.empty()) {
279 DVLOG(1) << "Extension candidates list empty";
280 return;
283 // For each file read the json description & build the proper
284 // associated prefs.
285 for (std::set<base::FilePath>::const_iterator it = candidates.begin();
286 it != candidates.end();
287 ++it) {
288 base::FilePath extension_candidate_path = base_path_.Append(*it);
290 std::string id =
291 #if defined(OS_WIN)
292 base::UTF16ToASCII(
293 extension_candidate_path.RemoveExtension().BaseName().value());
294 #elif defined(OS_POSIX)
295 extension_candidate_path.RemoveExtension().BaseName().value();
296 #endif
298 DVLOG(1) << "Reading json file: "
299 << extension_candidate_path.LossyDisplayName();
301 JSONFileValueDeserializer deserializer(extension_candidate_path);
302 scoped_ptr<base::DictionaryValue> ext_prefs(
303 ExtractExtensionPrefs(&deserializer, extension_candidate_path));
304 if (ext_prefs) {
305 DVLOG(1) << "Adding extension with id: " << id;
306 prefs->Set(id, ext_prefs.release());
311 ExternalTestingLoader::ExternalTestingLoader(
312 const std::string& json_data,
313 const base::FilePath& fake_base_path)
314 : fake_base_path_(fake_base_path) {
315 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316 JSONStringValueDeserializer deserializer(json_data);
317 base::FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
318 testing_prefs_.reset(ExtractExtensionPrefs(&deserializer, fake_json_path));
321 void ExternalTestingLoader::StartLoading() {
322 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 prefs_.reset(testing_prefs_->DeepCopy());
324 LoadFinished();
327 ExternalTestingLoader::~ExternalTestingLoader() {}
329 const base::FilePath ExternalTestingLoader::GetBaseCrxFilePath() {
330 return fake_base_path_;
333 } // namespace extensions