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