| 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_extension_provider_impl.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "base/file_path.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/memory/linked_ptr.h" | |
| 11 #include "base/metrics/field_trial.h" | |
| 12 #include "base/path_service.h" | |
| 13 #include "base/string_util.h" | |
| 14 #include "base/values.h" | |
| 15 #include "base/version.h" | |
| 16 #include "chrome/browser/browser_process.h" | |
| 17 #include "chrome/browser/extensions/extension_service.h" | |
| 18 #include "chrome/browser/extensions/external_extension_provider_interface.h" | |
| 19 #include "chrome/browser/extensions/external_policy_extension_loader.h" | |
| 20 #include "chrome/browser/extensions/external_pref_extension_loader.h" | |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 22 #include "chrome/common/chrome_paths.h" | |
| 23 #include "chrome/common/chrome_switches.h" | |
| 24 #include "chrome/common/pref_names.h" | |
| 25 #include "content/public/browser/browser_thread.h" | |
| 26 #include "ui/base/l10n/l10n_util.h" | |
| 27 | |
| 28 #if defined(OS_CHROMEOS) | |
| 29 #include "chrome/browser/chromeos/login/user_manager.h" | |
| 30 #include "chrome/browser/policy/app_pack_updater.h" | |
| 31 #include "chrome/browser/policy/browser_policy_connector.h" | |
| 32 #endif | |
| 33 | |
| 34 #if !defined(OS_CHROMEOS) | |
| 35 #include "chrome/browser/extensions/default_apps.h" | |
| 36 #endif | |
| 37 | |
| 38 #if defined(OS_WIN) | |
| 39 #include "chrome/browser/extensions/external_registry_extension_loader_win.h" | |
| 40 #endif | |
| 41 | |
| 42 using content::BrowserThread; | |
| 43 using extensions::Extension; | |
| 44 | |
| 45 // Constants for keeping track of extension preferences in a dictionary. | |
| 46 const char ExternalExtensionProviderImpl::kExternalCrx[] = "external_crx"; | |
| 47 const char ExternalExtensionProviderImpl::kExternalVersion[] = | |
| 48 "external_version"; | |
| 49 const char ExternalExtensionProviderImpl::kExternalUpdateUrl[] = | |
| 50 "external_update_url"; | |
| 51 const char ExternalExtensionProviderImpl::kSupportedLocales[] = | |
| 52 "supported_locales"; | |
| 53 const char ExternalExtensionProviderImpl::kIsBookmarkApp[] = | |
| 54 "is_bookmark_app"; | |
| 55 | |
| 56 ExternalExtensionProviderImpl::ExternalExtensionProviderImpl( | |
| 57 VisitorInterface* service, | |
| 58 ExternalExtensionLoader* loader, | |
| 59 Extension::Location crx_location, | |
| 60 Extension::Location download_location, | |
| 61 int creation_flags) | |
| 62 : crx_location_(crx_location), | |
| 63 download_location_(download_location), | |
| 64 service_(service), | |
| 65 prefs_(NULL), | |
| 66 ready_(false), | |
| 67 loader_(loader), | |
| 68 creation_flags_(creation_flags), | |
| 69 auto_acknowledge_(false) { | |
| 70 loader_->Init(this); | |
| 71 } | |
| 72 | |
| 73 ExternalExtensionProviderImpl::~ExternalExtensionProviderImpl() { | |
| 74 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 75 loader_->OwnerShutdown(); | |
| 76 } | |
| 77 | |
| 78 void ExternalExtensionProviderImpl::VisitRegisteredExtension() { | |
| 79 // The loader will call back to SetPrefs. | |
| 80 loader_->StartLoading(); | |
| 81 } | |
| 82 | |
| 83 void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) { | |
| 84 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 85 | |
| 86 // Check if the service is still alive. It is possible that it went | |
| 87 // away while |loader_| was working on the FILE thread. | |
| 88 if (!service_) return; | |
| 89 | |
| 90 prefs_.reset(prefs); | |
| 91 ready_ = true; // Queries for extensions are allowed from this point. | |
| 92 | |
| 93 // Set of unsupported extensions that need to be deleted from prefs_. | |
| 94 std::set<std::string> unsupported_extensions; | |
| 95 | |
| 96 // Notify ExtensionService about all the extensions this provider has. | |
| 97 for (DictionaryValue::key_iterator i = prefs_->begin_keys(); | |
| 98 i != prefs_->end_keys(); ++i) { | |
| 99 const std::string& extension_id = *i; | |
| 100 DictionaryValue* extension; | |
| 101 | |
| 102 if (!Extension::IdIsValid(extension_id)) { | |
| 103 LOG(WARNING) << "Malformed extension dictionary: key " | |
| 104 << extension_id.c_str() << " is not a valid id."; | |
| 105 continue; | |
| 106 } | |
| 107 | |
| 108 if (!prefs_->GetDictionaryWithoutPathExpansion(extension_id, &extension)) { | |
| 109 LOG(WARNING) << "Malformed extension dictionary: key " | |
| 110 << extension_id.c_str() | |
| 111 << " has a value that is not a dictionary."; | |
| 112 continue; | |
| 113 } | |
| 114 | |
| 115 FilePath::StringType external_crx; | |
| 116 Value* external_version_value; | |
| 117 std::string external_version; | |
| 118 std::string external_update_url; | |
| 119 | |
| 120 bool has_external_crx = extension->GetString(kExternalCrx, &external_crx); | |
| 121 | |
| 122 bool has_external_version = false; | |
| 123 if (extension->Get(kExternalVersion, &external_version_value)) { | |
| 124 if (external_version_value->IsType(Value::TYPE_STRING)) { | |
| 125 external_version_value->GetAsString(&external_version); | |
| 126 has_external_version = true; | |
| 127 } else { | |
| 128 LOG(WARNING) << "Malformed extension dictionary for extension: " | |
| 129 << extension_id.c_str() << ". " << kExternalVersion | |
| 130 << " value must be a string."; | |
| 131 continue; | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 bool has_external_update_url = extension->GetString(kExternalUpdateUrl, | |
| 136 &external_update_url); | |
| 137 if (has_external_crx != has_external_version) { | |
| 138 LOG(WARNING) << "Malformed extension dictionary for extension: " | |
| 139 << extension_id.c_str() << ". " << kExternalCrx | |
| 140 << " and " << kExternalVersion << " must be used together."; | |
| 141 continue; | |
| 142 } | |
| 143 | |
| 144 if (has_external_crx == has_external_update_url) { | |
| 145 LOG(WARNING) << "Malformed extension dictionary for extension: " | |
| 146 << extension_id.c_str() << ". Exactly one of the " | |
| 147 << "followng keys should be used: " << kExternalCrx | |
| 148 << ", " << kExternalUpdateUrl << "."; | |
| 149 continue; | |
| 150 } | |
| 151 | |
| 152 // Check that extension supports current browser locale. | |
| 153 ListValue* supported_locales = NULL; | |
| 154 if (extension->GetList(kSupportedLocales, &supported_locales)) { | |
| 155 std::vector<std::string> browser_locales; | |
| 156 l10n_util::GetParentLocales(g_browser_process->GetApplicationLocale(), | |
| 157 &browser_locales); | |
| 158 | |
| 159 size_t num_locales = supported_locales->GetSize(); | |
| 160 bool locale_supported = false; | |
| 161 for (size_t j = 0; j < num_locales; j++) { | |
| 162 std::string current_locale; | |
| 163 if (supported_locales->GetString(j, ¤t_locale) && | |
| 164 l10n_util::IsValidLocaleSyntax(current_locale)) { | |
| 165 current_locale = l10n_util::NormalizeLocale(current_locale); | |
| 166 if (std::find(browser_locales.begin(), browser_locales.end(), | |
| 167 current_locale) != browser_locales.end()) { | |
| 168 locale_supported = true; | |
| 169 break; | |
| 170 } | |
| 171 } else { | |
| 172 LOG(WARNING) << "Unrecognized locale '" << current_locale | |
| 173 << "' found as supported locale for extension: " | |
| 174 << extension_id; | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 if (!locale_supported) { | |
| 179 unsupported_extensions.insert(extension_id); | |
| 180 LOG(INFO) << "Skip installing (or uninstall) external extension: " | |
| 181 << extension_id << " because the extension doesn't support " | |
| 182 << "the browser locale."; | |
| 183 continue; | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 int creation_flags = creation_flags_; | |
| 188 bool is_bookmark_app; | |
| 189 if (extension->GetBoolean(kIsBookmarkApp, &is_bookmark_app) && | |
| 190 is_bookmark_app) { | |
| 191 creation_flags |= Extension::FROM_BOOKMARK; | |
| 192 } | |
| 193 | |
| 194 if (has_external_crx) { | |
| 195 if (crx_location_ == Extension::INVALID) { | |
| 196 LOG(WARNING) << "This provider does not support installing external " | |
| 197 << "extensions from crx files."; | |
| 198 continue; | |
| 199 } | |
| 200 if (external_crx.find(FilePath::kParentDirectory) != | |
| 201 base::StringPiece::npos) { | |
| 202 LOG(WARNING) << "Path traversal not allowed in path: " | |
| 203 << external_crx.c_str(); | |
| 204 continue; | |
| 205 } | |
| 206 | |
| 207 // If the path is relative, and the provider has a base path, | |
| 208 // build the absolute path to the crx file. | |
| 209 FilePath path(external_crx); | |
| 210 if (!path.IsAbsolute()) { | |
| 211 FilePath base_path = loader_->GetBaseCrxFilePath(); | |
| 212 if (base_path.empty()) { | |
| 213 LOG(WARNING) << "File path " << external_crx.c_str() | |
| 214 << " is relative. An absolute path is required."; | |
| 215 continue; | |
| 216 } | |
| 217 path = base_path.Append(external_crx); | |
| 218 } | |
| 219 | |
| 220 Version version(external_version); | |
| 221 if (!version.IsValid()) { | |
| 222 LOG(WARNING) << "Malformed extension dictionary for extension: " | |
| 223 << extension_id.c_str() << ". Invalid version string \"" | |
| 224 << external_version << "\"."; | |
| 225 continue; | |
| 226 } | |
| 227 service_->OnExternalExtensionFileFound(extension_id, &version, path, | |
| 228 crx_location_, creation_flags, | |
| 229 auto_acknowledge_); | |
| 230 } else { // if (has_external_update_url) | |
| 231 CHECK(has_external_update_url); // Checking of keys above ensures this. | |
| 232 if (download_location_ == Extension::INVALID) { | |
| 233 LOG(WARNING) << "This provider does not support installing external " | |
| 234 << "extensions from update URLs."; | |
| 235 continue; | |
| 236 } | |
| 237 GURL update_url(external_update_url); | |
| 238 if (!update_url.is_valid()) { | |
| 239 LOG(WARNING) << "Malformed extension dictionary for extension: " | |
| 240 << extension_id.c_str() << ". Key " << kExternalUpdateUrl | |
| 241 << " has value \"" << external_update_url | |
| 242 << "\", which is not a valid URL."; | |
| 243 continue; | |
| 244 } | |
| 245 service_->OnExternalExtensionUpdateUrlFound( | |
| 246 extension_id, update_url, download_location_); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 for (std::set<std::string>::iterator it = unsupported_extensions.begin(); | |
| 251 it != unsupported_extensions.end(); ++it) { | |
| 252 // Remove extension for the list of know external extensions. The extension | |
| 253 // will be uninstalled later because provider doesn't provide it anymore. | |
| 254 prefs_->Remove(*it, NULL); | |
| 255 } | |
| 256 | |
| 257 service_->OnExternalProviderReady(this); | |
| 258 } | |
| 259 | |
| 260 void ExternalExtensionProviderImpl::ServiceShutdown() { | |
| 261 service_ = NULL; | |
| 262 } | |
| 263 | |
| 264 bool ExternalExtensionProviderImpl::IsReady() const { | |
| 265 return ready_; | |
| 266 } | |
| 267 | |
| 268 bool ExternalExtensionProviderImpl::HasExtension( | |
| 269 const std::string& id) const { | |
| 270 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 271 CHECK(prefs_.get()); | |
| 272 CHECK(ready_); | |
| 273 return prefs_->HasKey(id); | |
| 274 } | |
| 275 | |
| 276 bool ExternalExtensionProviderImpl::GetExtensionDetails( | |
| 277 const std::string& id, Extension::Location* location, | |
| 278 scoped_ptr<Version>* version) const { | |
| 279 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 280 CHECK(prefs_.get()); | |
| 281 CHECK(ready_); | |
| 282 DictionaryValue* extension = NULL; | |
| 283 if (!prefs_->GetDictionary(id, &extension)) | |
| 284 return false; | |
| 285 | |
| 286 Extension::Location loc = Extension::INVALID; | |
| 287 if (extension->HasKey(kExternalUpdateUrl)) { | |
| 288 loc = download_location_; | |
| 289 | |
| 290 } else if (extension->HasKey(kExternalCrx)) { | |
| 291 loc = crx_location_; | |
| 292 | |
| 293 std::string external_version; | |
| 294 if (!extension->GetString(kExternalVersion, &external_version)) | |
| 295 return false; | |
| 296 | |
| 297 if (version) | |
| 298 version->reset(new Version(external_version)); | |
| 299 | |
| 300 } else { | |
| 301 NOTREACHED(); // Chrome should not allow prefs to get into this state. | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 if (location) | |
| 306 *location = loc; | |
| 307 | |
| 308 return true; | |
| 309 } | |
| 310 | |
| 311 // static | |
| 312 void ExternalExtensionProviderImpl::CreateExternalProviders( | |
| 313 VisitorInterface* service, | |
| 314 Profile* profile, | |
| 315 ProviderCollection* provider_list) { | |
| 316 | |
| 317 // On Mac OS, items in /Library/... should be written by the superuser. | |
| 318 // Check that all components of the path are writable by root only. | |
| 319 ExternalPrefExtensionLoader::Options check_admin_permissions_on_mac; | |
| 320 #if defined(OS_MACOSX) | |
| 321 check_admin_permissions_on_mac = | |
| 322 ExternalPrefExtensionLoader::ENSURE_PATH_CONTROLLED_BY_ADMIN; | |
| 323 #else | |
| 324 check_admin_permissions_on_mac = ExternalPrefExtensionLoader::NONE; | |
| 325 #endif | |
| 326 | |
| 327 bool is_chromeos_demo_session = false; | |
| 328 int bundled_extension_creation_flags = Extension::NO_FLAGS; | |
| 329 #if defined(OS_CHROMEOS) | |
| 330 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); | |
| 331 is_chromeos_demo_session = | |
| 332 user_manager && user_manager->IsLoggedInAsDemoUser() && | |
| 333 g_browser_process->browser_policy_connector()->GetDeviceMode() == | |
| 334 policy::DEVICE_MODE_KIOSK; | |
| 335 bundled_extension_creation_flags = Extension::FROM_WEBSTORE; | |
| 336 #endif | |
| 337 | |
| 338 if (!is_chromeos_demo_session) { | |
| 339 provider_list->push_back( | |
| 340 linked_ptr<ExternalExtensionProviderInterface>( | |
| 341 new ExternalExtensionProviderImpl( | |
| 342 service, | |
| 343 new ExternalPrefExtensionLoader( | |
| 344 chrome::DIR_EXTERNAL_EXTENSIONS, | |
| 345 check_admin_permissions_on_mac), | |
| 346 Extension::EXTERNAL_PREF, | |
| 347 Extension::EXTERNAL_PREF_DOWNLOAD, | |
| 348 bundled_extension_creation_flags))); | |
| 349 } | |
| 350 | |
| 351 #if defined(OS_CHROMEOS) || defined (OS_MACOSX) | |
| 352 // Define a per-user source of external extensions. | |
| 353 // On Chrome OS, this serves as a source for OEM customization. | |
| 354 provider_list->push_back( | |
| 355 linked_ptr<ExternalExtensionProviderInterface>( | |
| 356 new ExternalExtensionProviderImpl( | |
| 357 service, | |
| 358 new ExternalPrefExtensionLoader( | |
| 359 chrome::DIR_USER_EXTERNAL_EXTENSIONS, | |
| 360 ExternalPrefExtensionLoader::NONE), | |
| 361 Extension::EXTERNAL_PREF, | |
| 362 Extension::EXTERNAL_PREF_DOWNLOAD, | |
| 363 Extension::NO_FLAGS))); | |
| 364 #endif | |
| 365 #if defined(OS_WIN) | |
| 366 provider_list->push_back( | |
| 367 linked_ptr<ExternalExtensionProviderInterface>( | |
| 368 new ExternalExtensionProviderImpl( | |
| 369 service, | |
| 370 new ExternalRegistryExtensionLoader, | |
| 371 Extension::EXTERNAL_REGISTRY, | |
| 372 Extension::INVALID, | |
| 373 Extension::NO_FLAGS))); | |
| 374 #endif | |
| 375 | |
| 376 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
| 377 provider_list->push_back( | |
| 378 linked_ptr<ExternalExtensionProviderInterface>( | |
| 379 new ExternalExtensionProviderImpl( | |
| 380 service, | |
| 381 new ExternalPrefExtensionLoader( | |
| 382 chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS, | |
| 383 ExternalPrefExtensionLoader::NONE), | |
| 384 Extension::EXTERNAL_PREF, | |
| 385 Extension::EXTERNAL_PREF_DOWNLOAD, | |
| 386 Extension::NO_FLAGS))); | |
| 387 #endif | |
| 388 | |
| 389 provider_list->push_back( | |
| 390 linked_ptr<ExternalExtensionProviderInterface>( | |
| 391 new ExternalExtensionProviderImpl( | |
| 392 service, | |
| 393 new ExternalPolicyExtensionLoader(profile), | |
| 394 Extension::INVALID, | |
| 395 Extension::EXTERNAL_POLICY_DOWNLOAD, | |
| 396 Extension::NO_FLAGS))); | |
| 397 | |
| 398 #if !defined(OS_CHROMEOS) | |
| 399 provider_list->push_back( | |
| 400 linked_ptr<ExternalExtensionProviderInterface>( | |
| 401 new default_apps::Provider( | |
| 402 profile, | |
| 403 service, | |
| 404 new ExternalPrefExtensionLoader( | |
| 405 chrome::DIR_DEFAULT_APPS, | |
| 406 ExternalPrefExtensionLoader::NONE), | |
| 407 Extension::EXTERNAL_PREF, | |
| 408 Extension::INVALID, | |
| 409 Extension::FROM_WEBSTORE))); | |
| 410 #endif | |
| 411 | |
| 412 #if defined(OS_CHROMEOS) | |
| 413 policy::BrowserPolicyConnector* connector = | |
| 414 g_browser_process->browser_policy_connector(); | |
| 415 if (is_chromeos_demo_session && connector->GetAppPackUpdater()) { | |
| 416 provider_list->push_back( | |
| 417 linked_ptr<ExternalExtensionProviderInterface>( | |
| 418 new ExternalExtensionProviderImpl( | |
| 419 service, | |
| 420 connector->GetAppPackUpdater()->CreateExternalExtensionLoader(), | |
| 421 Extension::EXTERNAL_PREF, | |
| 422 Extension::INVALID, | |
| 423 Extension::NO_FLAGS))); | |
| 424 } | |
| 425 #endif | |
| 426 } | |
| OLD | NEW |