OLD | NEW |
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 } |
OLD | NEW |