Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(304)

Side by Side Diff: chrome/browser/extensions/external_pref_extension_loader.cc

Issue 9963120: Introduces an additional extension loader that load extra extensions based on per-extension json fi… (Closed) Base URL: https://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/external_pref_extension_loader.h" 5 #include "chrome/browser/extensions/external_pref_extension_loader.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/dir_reader_posix.h"
8 #include "base/file_path.h" 9 #include "base/file_path.h"
9 #include "base/file_util.h" 10 #include "base/file_util.h"
10 #include "base/json/json_file_value_serializer.h" 11 #include "base/json/json_file_value_serializer.h"
11 #include "base/json/json_string_value_serializer.h" 12 #include "base/json/json_string_value_serializer.h"
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
14 #include "base/path_service.h" 15 #include "base/path_service.h"
15 #include "chrome/common/chrome_paths.h" 16 #include "chrome/common/chrome_paths.h"
16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/browser_thread.h"
17 18
18 using content::BrowserThread; 19 using content::BrowserThread;
19 20
20 namespace { 21 namespace {
21 22
23 std::set<FilePath> GetPrefsCandidateFilesFromFolder(
24 const FilePath& external_extension_search_path) {
25 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
26
27 std::set<FilePath> external_extension_paths;
28
29 if (!file_util::PathExists(external_extension_search_path)) {
30 // Does not have to exist.
31 return external_extension_paths;
32 }
33
34 base::DirReaderPosix
35 reader(external_extension_search_path.value().c_str());
36
37 if (!reader.IsValid()) {
38 LOG(ERROR) << "Can not read external extensions path '"
39 << external_extension_search_path.LossyDisplayName()
40 << "' although it appears to exist.";
41 return external_extension_paths;
42 }
43
44 while (reader.Next()) {
45 const FilePath filename(reader.name());
46
47 if (filename.MatchesExtension(".json")) {
48 external_extension_paths.insert(filename);
49 } else {
50 DVLOG(1) << "Not considering: " << reader.name()
51 << " (does not have a .json extension)";
52 }
53 }
54
55 return external_extension_paths;
56 }
57
58 // Extracts extension information from a json file serialized by |serializer|.
59 // |path| is only used for informational purposes (outputted when an error
60 // occurs). An empty dictionary is returned in case of failure (e.g. invalid
61 // path or json content).
22 // Caller takes ownership of the returned dictionary. 62 // Caller takes ownership of the returned dictionary.
23 DictionaryValue* ExtractPrefs(const FilePath& path, 63 DictionaryValue* ExtractExtensionPrefs(base::ValueSerializer* serializer,
24 base::ValueSerializer* serializer) { 64 const FilePath& path) {
25 std::string error_msg; 65 std::string error_msg;
26 Value* extensions = serializer->Deserialize(NULL, &error_msg); 66 Value* extensions = serializer->Deserialize(NULL, &error_msg);
27 if (!extensions) { 67 if (!extensions) {
28 LOG(WARNING) << "Unable to deserialize json data: " << error_msg 68 LOG(WARNING) << "Unable to deserialize json data: " << error_msg
29 << " In file " << path.value() << " ."; 69 << " in file " << path.value() << ".";
30 } else { 70 return new DictionaryValue;
31 if (!extensions->IsType(Value::TYPE_DICTIONARY)) {
32 LOG(WARNING) << "Expected a JSON dictionary in file "
33 << path.value() << " .";
34 } else {
35 return static_cast<DictionaryValue*>(extensions);
36 }
37 } 71 }
72
73 DictionaryValue* ext_dictionary = NULL;
74 if (extensions->GetAsDictionary(&ext_dictionary))
75 return ext_dictionary;
76
77 LOG(WARNING) << "Expected a JSON dictionary in file "
78 << path.value() << ".";
38 return new DictionaryValue; 79 return new DictionaryValue;
39 } 80 }
40 81
41 } // namespace 82 } // namespace {
42 83
43 ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_key, 84 ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_id,
44 Options options) 85 Options options)
45 : base_path_key_(base_path_key), 86 : base_path_id_(base_path_id),
46 options_(options){ 87 options_(options) {
47 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 88 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
48 } 89 }
49 90
50 const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() { 91 const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() {
51 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 92 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 93
53 // |base_path_| was set in LoadOnFileThread(). 94 // |base_path_| was set in LoadOnFileThread().
54 return base_path_; 95 return base_path_;
55 } 96 }
56 97
57 void ExternalPrefExtensionLoader::StartLoading() { 98 void ExternalPrefExtensionLoader::StartLoading() {
58 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 99 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59 BrowserThread::PostTask( 100 BrowserThread::PostTask(
60 BrowserThread::FILE, FROM_HERE, 101 BrowserThread::FILE, FROM_HERE,
61 base::Bind(&ExternalPrefExtensionLoader::LoadOnFileThread, this)); 102 base::Bind(&ExternalPrefExtensionLoader::LoadOnFileThread, this));
62 } 103 }
63 104
64 DictionaryValue* ExternalPrefExtensionLoader::ReadJsonPrefsFile() { 105 void ExternalPrefExtensionLoader::LoadOnFileThread() {
65 // TODO(skerner): Some values of base_path_key_ will cause 106 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
107
108 // TODO(skerner): Some values of base_path_id_ will cause
66 // PathService::Get() to return false, because the path does 109 // PathService::Get() to return false, because the path does
67 // not exist. Find and fix the build/install scripts so that 110 // not exist. Find and fix the build/install scripts so that
68 // this can become a CHECK(). Known examples include chrome 111 // this can become a CHECK(). Known examples include chrome
69 // OS developer builds and linux install packages. 112 // OS developer builds and linux install packages.
70 // Tracked as crbug.com/70402 . 113 // Tracked as crbug.com/70402 .
71 if (!PathService::Get(base_path_key_, &base_path_)) { 114 if (!PathService::Get(base_path_id_, &base_path_))
72 return NULL; 115 return;
116
117 scoped_ptr<DictionaryValue> prefs(new DictionaryValue);
118
119 ReadExternalExtensionPrefFile(prefs.get());
120 if (!prefs.empty())
121 LOG(WARNING) << "You are using an old-style extension deployment method "
122 "(external_extensions.json), which will soon be "
123 "deprecated. (see http://code.google.com/chrome/"
124 "extensions/external_extensions.html )";
125
126 ReadStandaloneExtensionPrefFiles(prefs.get());
127
128 prefs_.reset(prefs.release());
129 if (!prefs_.get())
130 prefs_.reset(new DictionaryValue());
131
132 if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
133 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
134 prefs_->size());
73 } 135 }
74 136
137 // If we have any records to process, then we must have
138 // read at least one .json file. If so, then we should have
139 // set |base_path_|.
140 if (!prefs_->empty())
141 CHECK(!base_path_.empty());
142
143 BrowserThread::PostTask(
144 BrowserThread::UI, FROM_HERE,
145 base::Bind(&ExternalPrefExtensionLoader::LoadFinished, this));
146 }
147
148 void ExternalPrefExtensionLoader::ReadExternalExtensionPrefFile(
149 DictionaryValue* prefs) {
150 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
151 CHECK(NULL != prefs);
152
75 FilePath json_file = base_path_.Append( 153 FilePath json_file = base_path_.Append(
76 FILE_PATH_LITERAL("external_extensions.json")); 154 FILE_PATH_LITERAL("external_extensions.json"));
77 155
78 if (!file_util::PathExists(json_file)) { 156 if (!file_util::PathExists(json_file)) {
79 // This is not an error. The file does not exist by default. 157 // This is not an error. The file does not exist by default.
80 return NULL; 158 return;
81 } 159 }
82 160
83 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) { 161 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) {
84 #if defined(OS_MACOSX) 162 #if defined(OS_MACOSX)
85 if (!file_util::VerifyPathControlledByAdmin(json_file)) { 163 if (!file_util::VerifyPathControlledByAdmin(json_file)) {
86 LOG(ERROR) << "Can not read external extensions source. The file " 164 LOG(ERROR) << "Can not read external extensions source. The file "
87 << json_file.value() << " and every directory in its path, " 165 << json_file.value() << " and every directory in its path, "
88 << "must be owned by root, have group \"admin\", and not be " 166 << "must be owned by root, have group \"admin\", and not be "
89 << "writable by all users. These restrictions prevent " 167 << "writable by all users. These restrictions prevent "
90 << "unprivleged users from making chrome install extensions " 168 << "unprivleged users from making chrome install extensions "
91 << "on other users' accounts."; 169 << "on other users' accounts.";
92 return NULL; 170 return NULL;
93 } 171 }
94 #else 172 #else
95 // The only platform that uses this check is Mac OS. If you add one, 173 // The only platform that uses this check is Mac OS. If you add one,
96 // you need to implement file_util::VerifyPathControlledByAdmin() for 174 // you need to implement file_util::VerifyPathControlledByAdmin() for
97 // that platform. 175 // that platform.
98 NOTREACHED(); 176 NOTREACHED();
99 #endif // defined(OS_MACOSX) 177 #endif // defined(OS_MACOSX)
100 } 178 }
101 179
102 JSONFileValueSerializer serializer(json_file); 180 JSONFileValueSerializer serializer(json_file);
103 DictionaryValue* parsed_json_prefs = ExtractPrefs(json_file, &serializer); 181 DictionaryValue * ext_prefs = ExtractExtensionPrefs(&serializer, json_file);
104 return parsed_json_prefs; 182 if (ext_prefs)
183 prefs->MergeDictionary(ext_prefs);
105 } 184 }
106 185
107 void ExternalPrefExtensionLoader::LoadOnFileThread() { 186 void ExternalPrefExtensionLoader::ReadStandaloneExtensionPrefFiles(
187 DictionaryValue* prefs) {
108 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 188 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
189 CHECK(NULL != prefs);
109 190
110 prefs_.reset(ReadJsonPrefsFile()); 191 // First list the potential .json candidates.
111 if (!prefs_.get()) 192 std::set<FilePath>
112 prefs_.reset(new DictionaryValue()); 193 candidates = GetPrefsCandidateFilesFromFolder(base_path_);
113 194 if (candidates.empty()) {
114 if (base_path_key_ == chrome::DIR_EXTERNAL_EXTENSIONS) { 195 DVLOG(1) << "Extension candidates list empty";
115 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount", 196 return;
116 prefs_->size());
117 } 197 }
118 198
119 // If we have any records to process, then we must have 199 // For each file read the json description & build the proper
120 // read the .json file. If we read the .json file, then 200 // associated prefs.
121 // we were should have set |base_path_|. 201 for (std::set<FilePath>::const_iterator it = candidates.begin();
122 if (!prefs_->empty()) 202 it != candidates.end();
123 CHECK(!base_path_.empty()); 203 ++it) {
204 FilePath extension_candidate_path = base_path_.Append(*it);
124 205
125 BrowserThread::PostTask( 206 FilePath::StringType id =
126 BrowserThread::UI, FROM_HERE, 207 extension_candidate_path.RemoveExtension().BaseName().value();
127 base::Bind(&ExternalPrefExtensionLoader::LoadFinished, this)); 208
209 DVLOG(1) << "Reading json file: " << extension_candidate_path.value();
210
211 JSONFileValueSerializer serializer(extension_candidate_path);
212 DictionaryValue* ext_prefs =
213 ExtractExtensionPrefs(&serializer, extension_candidate_path);
214
215 if (ext_prefs) {
216 DVLOG(1) << "Adding extension with id: " << id;
217 prefs->Set(id, ext_prefs);
218 }
219 }
128 } 220 }
129 221
130 ExternalTestingExtensionLoader::ExternalTestingExtensionLoader( 222 ExternalTestingExtensionLoader::ExternalTestingExtensionLoader(
131 const std::string& json_data, 223 const std::string& json_data,
132 const FilePath& fake_base_path) 224 const FilePath& fake_base_path)
133 : fake_base_path_(fake_base_path) { 225 : fake_base_path_(fake_base_path) {
134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 226 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135 JSONStringValueSerializer serializer(json_data); 227 JSONStringValueSerializer serializer(json_data);
136 FilePath fake_json_path = fake_base_path.AppendASCII("fake.json"); 228 FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
137 testing_prefs_.reset(ExtractPrefs(fake_json_path, &serializer)); 229 testing_prefs_.reset(ExtractExtensionPrefs(&serializer, fake_json_path));
138 } 230 }
139 231
140 void ExternalTestingExtensionLoader::StartLoading() { 232 void ExternalTestingExtensionLoader::StartLoading() {
141 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 233 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 prefs_.reset(testing_prefs_->DeepCopy()); 234 prefs_.reset(testing_prefs_->DeepCopy());
143 LoadFinished(); 235 LoadFinished();
144 } 236 }
145 237
146 ExternalTestingExtensionLoader::~ExternalTestingExtensionLoader() {} 238 ExternalTestingExtensionLoader::~ExternalTestingExtensionLoader() {}
147 239
148 const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() { 240 const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() {
149 return fake_base_path_; 241 return fake_base_path_;
150 } 242 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/external_pref_extension_loader.h ('k') | chrome/common/chrome_paths.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698