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

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

Issue 10260010: Modifies the external extension preferences loader to load not only external_extensions.json but al… (Closed) Base URL: svn://svn.chromium.org/chrome/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 | Annotate | Revision Log
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"
16 #include "base/string_util.h"
17 #include "base/utf_string_conversions.h"
15 #include "chrome/common/chrome_paths.h" 18 #include "chrome/common/chrome_paths.h"
16 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/browser_thread.h"
17 20
18 using content::BrowserThread; 21 using content::BrowserThread;
19 22
20 namespace { 23 namespace {
21 24
25 FilePath::CharType kExternalExtensionJson[] =
26 FILE_PATH_LITERAL("external_extensions.json");
27
28 std::set<FilePath> GetPrefsCandidateFilesFromFolder(
29 const FilePath& external_extension_search_path) {
30 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
31
32 std::set<FilePath> external_extension_paths;
33
34 if (!file_util::PathExists(external_extension_search_path)) {
35 // Does not have to exist.
36 return external_extension_paths;
37 }
38
39 file_util::FileEnumerator json_files(
40 external_extension_search_path,
41 false, // Recursive.
42 file_util::FileEnumerator::FILES);
43 #if defined(OS_WIN)
44 FilePath::StringType extension = UTF8ToWide(std::string(".json"));
45 #elif defined(OS_POSIX)
46 FilePath::StringType extension(".json");
47 #endif
48 do {
49 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)";
59 }
60 } while (true);
61
62 return external_extension_paths;
63 }
64
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).
22 // Caller takes ownership of the returned dictionary. 69 // Caller takes ownership of the returned dictionary.
23 DictionaryValue* ExtractPrefs(const FilePath& path, 70 DictionaryValue* ExtractExtensionPrefs(base::ValueSerializer* serializer,
24 base::ValueSerializer* serializer) { 71 const FilePath& path) {
25 std::string error_msg; 72 std::string error_msg;
26 Value* extensions = serializer->Deserialize(NULL, &error_msg); 73 Value* extensions = serializer->Deserialize(NULL, &error_msg);
27 if (!extensions) { 74 if (!extensions) {
28 LOG(WARNING) << "Unable to deserialize json data: " << error_msg 75 LOG(WARNING) << "Unable to deserialize json data: " << error_msg
29 << " In file " << path.value() << " ."; 76 << " in file " << path.value() << ".";
30 } else { 77 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 } 78 }
79
80 DictionaryValue* ext_dictionary = NULL;
81 if (extensions->GetAsDictionary(&ext_dictionary))
82 return ext_dictionary;
83
84 LOG(WARNING) << "Expected a JSON dictionary in file "
85 << path.value() << ".";
38 return new DictionaryValue; 86 return new DictionaryValue;
39 } 87 }
40 88
41 } // namespace 89 } // namespace
42 90
43 ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_key, 91 ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_id,
44 Options options) 92 Options options)
45 : base_path_key_(base_path_key), 93 : base_path_id_(base_path_id),
46 options_(options){ 94 options_(options) {
47 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 95 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
48 } 96 }
49 97
50 const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() { 98 const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() {
51 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 99 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 100
53 // |base_path_| was set in LoadOnFileThread(). 101 // |base_path_| was set in LoadOnFileThread().
54 return base_path_; 102 return base_path_;
55 } 103 }
56 104
57 void ExternalPrefExtensionLoader::StartLoading() { 105 void ExternalPrefExtensionLoader::StartLoading() {
58 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 106 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59 BrowserThread::PostTask( 107 BrowserThread::PostTask(
60 BrowserThread::FILE, FROM_HERE, 108 BrowserThread::FILE, FROM_HERE,
61 base::Bind(&ExternalPrefExtensionLoader::LoadOnFileThread, this)); 109 base::Bind(&ExternalPrefExtensionLoader::LoadOnFileThread, this));
62 } 110 }
63 111
64 DictionaryValue* ExternalPrefExtensionLoader::ReadJsonPrefsFile() { 112 void ExternalPrefExtensionLoader::LoadOnFileThread() {
65 // TODO(skerner): Some values of base_path_key_ will cause 113 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
114
115 // TODO(skerner): Some values of base_path_id_ will cause
66 // PathService::Get() to return false, because the path does 116 // PathService::Get() to return false, because the path does
67 // not exist. Find and fix the build/install scripts so that 117 // not exist. Find and fix the build/install scripts so that
68 // this can become a CHECK(). Known examples include chrome 118 // this can become a CHECK(). Known examples include chrome
69 // OS developer builds and linux install packages. 119 // OS developer builds and linux install packages.
70 // Tracked as crbug.com/70402 . 120 // Tracked as crbug.com/70402 .
71 if (!PathService::Get(base_path_key_, &base_path_)) { 121 if (!PathService::Get(base_path_id_, &base_path_))
72 return NULL; 122 return;
123
124 scoped_ptr<DictionaryValue> prefs(new DictionaryValue);
125
126 ReadExternalExtensionPrefFile(prefs.get());
127 if (!prefs->empty())
128 LOG(WARNING) << "You are using an old-style extension deployment method "
129 "(external_extensions.json), which will soon be "
130 "deprecated. (see http://code.google.com/chrome/"
131 "extensions/external_extensions.html )";
132
133 ReadStandaloneExtensionPrefFiles(prefs.get());
134
135 prefs_.reset(prefs.release());
136 if (!prefs_.get())
137 prefs_.reset(new DictionaryValue());
138
139 if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
140 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
141 prefs_->size());
73 } 142 }
74 143
75 FilePath json_file = base_path_.Append( 144 // If we have any records to process, then we must have
76 FILE_PATH_LITERAL("external_extensions.json")); 145 // read at least one .json file. If so, then we should have
146 // set |base_path_|.
147 if (!prefs_->empty())
148 CHECK(!base_path_.empty());
149
150 BrowserThread::PostTask(
151 BrowserThread::UI, FROM_HERE,
152 base::Bind(&ExternalPrefExtensionLoader::LoadFinished, this));
153 }
154
155 void ExternalPrefExtensionLoader::ReadExternalExtensionPrefFile(
156 DictionaryValue* prefs) {
157 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
158 CHECK(NULL != prefs);
159
160 FilePath json_file = base_path_.Append(kExternalExtensionJson);
77 161
78 if (!file_util::PathExists(json_file)) { 162 if (!file_util::PathExists(json_file)) {
79 // This is not an error. The file does not exist by default. 163 // This is not an error. The file does not exist by default.
80 return NULL; 164 return;
81 } 165 }
82 166
83 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) { 167 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) {
84 #if defined(OS_MACOSX) 168 #if defined(OS_MACOSX)
85 if (!file_util::VerifyPathControlledByAdmin(json_file)) { 169 if (!file_util::VerifyPathControlledByAdmin(json_file)) {
86 LOG(ERROR) << "Can not read external extensions source. The file " 170 LOG(ERROR) << "Can not read external extensions source. The file "
87 << json_file.value() << " and every directory in its path, " 171 << json_file.value() << " and every directory in its path, "
88 << "must be owned by root, have group \"admin\", and not be " 172 << "must be owned by root, have group \"admin\", and not be "
89 << "writable by all users. These restrictions prevent " 173 << "writable by all users. These restrictions prevent "
90 << "unprivleged users from making chrome install extensions " 174 << "unprivleged users from making chrome install extensions "
91 << "on other users' accounts."; 175 << "on other users' accounts.";
92 return NULL; 176 return;
93 } 177 }
94 #else 178 #else
95 // The only platform that uses this check is Mac OS. If you add one, 179 // The only platform that uses this check is Mac OS. If you add one,
96 // you need to implement file_util::VerifyPathControlledByAdmin() for 180 // you need to implement file_util::VerifyPathControlledByAdmin() for
97 // that platform. 181 // that platform.
98 NOTREACHED(); 182 NOTREACHED();
99 #endif // defined(OS_MACOSX) 183 #endif // defined(OS_MACOSX)
100 } 184 }
101 185
102 JSONFileValueSerializer serializer(json_file); 186 JSONFileValueSerializer serializer(json_file);
103 DictionaryValue* parsed_json_prefs = ExtractPrefs(json_file, &serializer); 187 DictionaryValue * ext_prefs = ExtractExtensionPrefs(&serializer, json_file);
104 return parsed_json_prefs; 188 if (ext_prefs)
189 prefs->MergeDictionary(ext_prefs);
105 } 190 }
106 191
107 void ExternalPrefExtensionLoader::LoadOnFileThread() { 192 void ExternalPrefExtensionLoader::ReadStandaloneExtensionPrefFiles(
193 DictionaryValue* prefs) {
108 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 194 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
195 CHECK(NULL != prefs);
109 196
110 prefs_.reset(ReadJsonPrefsFile()); 197 // First list the potential .json candidates.
111 if (!prefs_.get()) 198 std::set<FilePath>
112 prefs_.reset(new DictionaryValue()); 199 candidates = GetPrefsCandidateFilesFromFolder(base_path_);
113 200 if (candidates.empty()) {
114 if (base_path_key_ == chrome::DIR_EXTERNAL_EXTENSIONS) { 201 DVLOG(1) << "Extension candidates list empty";
115 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount", 202 return;
116 prefs_->size());
117 } 203 }
118 204
119 // If we have any records to process, then we must have 205 // For each file read the json description & build the proper
120 // read the .json file. If we read the .json file, then 206 // associated prefs.
121 // we were should have set |base_path_|. 207 for (std::set<FilePath>::const_iterator it = candidates.begin();
122 if (!prefs_->empty()) 208 it != candidates.end();
123 CHECK(!base_path_.empty()); 209 ++it) {
210 FilePath extension_candidate_path = base_path_.Append(*it);
124 211
125 BrowserThread::PostTask( 212 std::string id =
126 BrowserThread::UI, FROM_HERE, 213 #if defined(OS_WIN)
127 base::Bind(&ExternalPrefExtensionLoader::LoadFinished, this)); 214 WideToASCII(
215 extension_candidate_path.RemoveExtension().BaseName().value());
216 #elif defined(OS_POSIX)
217 extension_candidate_path.RemoveExtension().BaseName().value().c_str();
218 #endif
219
220 DVLOG(1) << "Reading json file: "
221 << extension_candidate_path.LossyDisplayName().c_str();
222
223 JSONFileValueSerializer serializer(extension_candidate_path);
224 DictionaryValue* ext_prefs =
225 ExtractExtensionPrefs(&serializer, extension_candidate_path);
226
227 if (ext_prefs) {
228 DVLOG(1) << "Adding extension with id: " << id;
229 prefs->Set(id, ext_prefs);
230 }
231 }
128 } 232 }
129 233
130 ExternalTestingExtensionLoader::ExternalTestingExtensionLoader( 234 ExternalTestingExtensionLoader::ExternalTestingExtensionLoader(
131 const std::string& json_data, 235 const std::string& json_data,
132 const FilePath& fake_base_path) 236 const FilePath& fake_base_path)
133 : fake_base_path_(fake_base_path) { 237 : fake_base_path_(fake_base_path) {
134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 238 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135 JSONStringValueSerializer serializer(json_data); 239 JSONStringValueSerializer serializer(json_data);
136 FilePath fake_json_path = fake_base_path.AppendASCII("fake.json"); 240 FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
137 testing_prefs_.reset(ExtractPrefs(fake_json_path, &serializer)); 241 testing_prefs_.reset(ExtractExtensionPrefs(&serializer, fake_json_path));
138 } 242 }
139 243
140 void ExternalTestingExtensionLoader::StartLoading() { 244 void ExternalTestingExtensionLoader::StartLoading() {
141 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 245 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 prefs_.reset(testing_prefs_->DeepCopy()); 246 prefs_.reset(testing_prefs_->DeepCopy());
143 LoadFinished(); 247 LoadFinished();
144 } 248 }
145 249
146 ExternalTestingExtensionLoader::~ExternalTestingExtensionLoader() {} 250 ExternalTestingExtensionLoader::~ExternalTestingExtensionLoader() {}
147 251
148 const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() { 252 const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() {
149 return fake_base_path_; 253 return fake_base_path_;
150 } 254 }
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