Prevent chrome://net-internals/#export from flickering
[chromium-blink-merge.git] / chrome / browser / extensions / external_pref_loader.cc
blob8e9a1336c898d39fc1c2537f61b37c4746c5b476
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.h"
20 #include "chrome/browser/sync/profile_sync_service_factory.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "content/public/browser/browser_thread.h"
24 using content::BrowserThread;
26 namespace {
28 base::FilePath::CharType kExternalExtensionJson[] =
29 FILE_PATH_LITERAL("external_extensions.json");
31 std::set<base::FilePath> GetPrefsCandidateFilesFromFolder(
32 const base::FilePath& external_extension_search_path) {
33 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
35 std::set<base::FilePath> external_extension_paths;
37 if (!base::PathExists(external_extension_search_path)) {
38 // Does not have to exist.
39 return external_extension_paths;
42 base::FileEnumerator json_files(
43 external_extension_search_path,
44 false, // Recursive.
45 base::FileEnumerator::FILES);
46 #if defined(OS_WIN)
47 base::FilePath::StringType extension = base::UTF8ToWide(".json");
48 #elif defined(OS_POSIX)
49 base::FilePath::StringType extension(".json");
50 #endif
51 do {
52 base::FilePath file = json_files.Next();
53 if (file.BaseName().value() == kExternalExtensionJson)
54 continue; // Already taken care of elsewhere.
55 if (file.empty())
56 break;
57 if (file.MatchesExtension(extension)) {
58 external_extension_paths.insert(file.BaseName());
59 } else {
60 DVLOG(1) << "Not considering: " << file.LossyDisplayName()
61 << " (does not have a .json extension)";
63 } while (true);
65 return external_extension_paths;
68 // Extracts extension information from a json file serialized by |serializer|.
69 // |path| is only used for informational purposes (outputted when an error
70 // occurs). An empty dictionary is returned in case of failure (e.g. invalid
71 // path or json content).
72 // Caller takes ownership of the returned dictionary.
73 base::DictionaryValue* ExtractExtensionPrefs(
74 base::ValueDeserializer* deserializer,
75 const base::FilePath& path) {
76 std::string error_msg;
77 base::Value* extensions = deserializer->Deserialize(NULL, &error_msg);
78 if (!extensions) {
79 LOG(WARNING) << "Unable to deserialize json data: " << error_msg
80 << " in file " << path.value() << ".";
81 return new base::DictionaryValue;
84 base::DictionaryValue* ext_dictionary = NULL;
85 if (extensions->GetAsDictionary(&ext_dictionary))
86 return ext_dictionary;
88 LOG(WARNING) << "Expected a JSON dictionary in file "
89 << path.value() << ".";
90 return new base::DictionaryValue;
93 } // namespace
95 namespace extensions {
97 ExternalPrefLoader::ExternalPrefLoader(int base_path_id,
98 Options options,
99 Profile* profile)
100 : base_path_id_(base_path_id),
101 options_(options),
102 profile_(profile),
103 syncable_pref_observer_(this) {
104 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107 ExternalPrefLoader::~ExternalPrefLoader() {
110 const base::FilePath ExternalPrefLoader::GetBaseCrxFilePath() {
111 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113 // |base_path_| was set in LoadOnFileThread().
114 return base_path_;
117 void ExternalPrefLoader::StartLoading() {
118 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
119 if ((options_ & DELAY_LOAD_UNTIL_PRIORITY_SYNC) &&
120 (profile_ && profile_->IsSyncAccessible())) {
121 if (!PostLoadIfPrioritySyncReady()) {
122 DCHECK(profile_);
123 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
124 DCHECK(prefs);
125 syncable_pref_observer_.Add(prefs);
126 ProfileSyncService* service =
127 ProfileSyncServiceFactory::GetForProfile(profile_);
128 DCHECK(service);
129 if (service->IsSyncEnabledAndLoggedIn() &&
130 (service->HasSyncSetupCompleted() ||
131 browser_defaults::kSyncAutoStarts)) {
132 service->AddObserver(this);
133 } else {
134 PostLoadAndRemoveObservers();
137 } else {
138 BrowserThread::PostTask(
139 BrowserThread::FILE, FROM_HERE,
140 base::Bind(&ExternalPrefLoader::LoadOnFileThread, this));
144 void ExternalPrefLoader::OnIsSyncingChanged() {
145 PostLoadIfPrioritySyncReady();
148 void ExternalPrefLoader::OnStateChanged() {
149 ProfileSyncService* service =
150 ProfileSyncServiceFactory::GetForProfile(profile_);
151 DCHECK(service);
152 if (!service->IsSyncEnabledAndLoggedIn()) {
153 PostLoadAndRemoveObservers();
157 bool ExternalPrefLoader::PostLoadIfPrioritySyncReady() {
158 DCHECK(options_ & DELAY_LOAD_UNTIL_PRIORITY_SYNC);
159 DCHECK(profile_);
161 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
162 DCHECK(prefs);
163 if (prefs->IsPrioritySyncing()) {
164 PostLoadAndRemoveObservers();
165 return true;
168 return false;
171 void ExternalPrefLoader::PostLoadAndRemoveObservers() {
172 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
173 DCHECK(prefs);
174 syncable_pref_observer_.Remove(prefs);
176 ProfileSyncService* service =
177 ProfileSyncServiceFactory::GetForProfile(profile_);
178 DCHECK(service);
179 service->RemoveObserver(this);
181 BrowserThread::PostTask(
182 BrowserThread::FILE, FROM_HERE,
183 base::Bind(&ExternalPrefLoader::LoadOnFileThread, this));
186 void ExternalPrefLoader::LoadOnFileThread() {
187 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
189 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
191 // TODO(skerner): Some values of base_path_id_ will cause
192 // PathService::Get() to return false, because the path does
193 // not exist. Find and fix the build/install scripts so that
194 // this can become a CHECK(). Known examples include chrome
195 // OS developer builds and linux install packages.
196 // Tracked as crbug.com/70402 .
197 if (PathService::Get(base_path_id_, &base_path_)) {
198 ReadExternalExtensionPrefFile(prefs.get());
200 if (!prefs->empty())
201 LOG(WARNING) << "You are using an old-style extension deployment method "
202 "(external_extensions.json), which will soon be "
203 "deprecated. (see http://developer.chrome.com/"
204 "extensions/external_extensions.html)";
206 ReadStandaloneExtensionPrefFiles(prefs.get());
209 prefs_.swap(prefs);
211 if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
212 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
213 prefs_->size());
216 // If we have any records to process, then we must have
217 // read at least one .json file. If so, then we should have
218 // set |base_path_|.
219 if (!prefs_->empty())
220 CHECK(!base_path_.empty());
222 BrowserThread::PostTask(
223 BrowserThread::UI, FROM_HERE,
224 base::Bind(&ExternalPrefLoader::LoadFinished, this));
227 void ExternalPrefLoader::ReadExternalExtensionPrefFile(
228 base::DictionaryValue* prefs) {
229 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
230 CHECK(NULL != prefs);
232 base::FilePath json_file = base_path_.Append(kExternalExtensionJson);
234 if (!base::PathExists(json_file)) {
235 // This is not an error. The file does not exist by default.
236 return;
239 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) {
240 #if defined(OS_MACOSX)
241 if (!base::VerifyPathControlledByAdmin(json_file)) {
242 LOG(ERROR) << "Can not read external extensions source. The file "
243 << json_file.value() << " and every directory in its path, "
244 << "must be owned by root, have group \"admin\", and not be "
245 << "writable by all users. These restrictions prevent "
246 << "unprivleged users from making chrome install extensions "
247 << "on other users' accounts.";
248 return;
250 #else
251 // The only platform that uses this check is Mac OS. If you add one,
252 // you need to implement base::VerifyPathControlledByAdmin() for
253 // that platform.
254 NOTREACHED();
255 #endif // defined(OS_MACOSX)
258 JSONFileValueDeserializer deserializer(json_file);
259 scoped_ptr<base::DictionaryValue> ext_prefs(
260 ExtractExtensionPrefs(&deserializer, json_file));
261 if (ext_prefs)
262 prefs->MergeDictionary(ext_prefs.get());
265 void ExternalPrefLoader::ReadStandaloneExtensionPrefFiles(
266 base::DictionaryValue* prefs) {
267 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
268 CHECK(NULL != prefs);
270 // First list the potential .json candidates.
271 std::set<base::FilePath>
272 candidates = GetPrefsCandidateFilesFromFolder(base_path_);
273 if (candidates.empty()) {
274 DVLOG(1) << "Extension candidates list empty";
275 return;
278 // For each file read the json description & build the proper
279 // associated prefs.
280 for (std::set<base::FilePath>::const_iterator it = candidates.begin();
281 it != candidates.end();
282 ++it) {
283 base::FilePath extension_candidate_path = base_path_.Append(*it);
285 std::string id =
286 #if defined(OS_WIN)
287 base::UTF16ToASCII(
288 extension_candidate_path.RemoveExtension().BaseName().value());
289 #elif defined(OS_POSIX)
290 extension_candidate_path.RemoveExtension().BaseName().value();
291 #endif
293 DVLOG(1) << "Reading json file: "
294 << extension_candidate_path.LossyDisplayName();
296 JSONFileValueDeserializer deserializer(extension_candidate_path);
297 scoped_ptr<base::DictionaryValue> ext_prefs(
298 ExtractExtensionPrefs(&deserializer, extension_candidate_path));
299 if (ext_prefs) {
300 DVLOG(1) << "Adding extension with id: " << id;
301 prefs->Set(id, ext_prefs.release());
306 ExternalTestingLoader::ExternalTestingLoader(
307 const std::string& json_data,
308 const base::FilePath& fake_base_path)
309 : fake_base_path_(fake_base_path) {
310 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 JSONStringValueDeserializer deserializer(json_data);
312 base::FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
313 testing_prefs_.reset(ExtractExtensionPrefs(&deserializer, fake_json_path));
316 void ExternalTestingLoader::StartLoading() {
317 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318 prefs_.reset(testing_prefs_->DeepCopy());
319 LoadFinished();
322 ExternalTestingLoader::~ExternalTestingLoader() {}
324 const base::FilePath ExternalTestingLoader::GetBaseCrxFilePath() {
325 return fake_base_path_;
328 } // namespace extensions