| OLD | NEW |
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 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/extension_assets_manager_chromeos.h" | 5 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 12 #include "base/memory/singleton.h" | 12 #include "base/memory/singleton.h" |
| 13 #include "base/prefs/pref_registry_simple.h" | 13 #include "base/prefs/pref_registry_simple.h" |
| 14 #include "base/prefs/pref_service.h" | 14 #include "base/prefs/pref_service.h" |
| 15 #include "base/prefs/scoped_user_pref_update.h" | 15 #include "base/prefs/scoped_user_pref_update.h" |
| 16 #include "base/sequenced_task_runner.h" | 16 #include "base/sequenced_task_runner.h" |
| 17 #include "base/sys_info.h" | 17 #include "base/sys_info.h" |
| 18 #include "chrome/browser/browser_process.h" | 18 #include "chrome/browser/browser_process.h" |
| 19 #include "chrome/browser/chromeos/login/users/user_manager.h" | |
| 20 #include "chrome/browser/extensions/extension_service.h" | 19 #include "chrome/browser/extensions/extension_service.h" |
| 21 #include "chrome/browser/profiles/profile.h" | 20 #include "chrome/browser/profiles/profile.h" |
| 22 #include "chromeos/chromeos_switches.h" | 21 #include "chromeos/chromeos_switches.h" |
| 23 #include "content/public/browser/browser_thread.h" | 22 #include "content/public/browser/browser_thread.h" |
| 24 #include "extensions/browser/extension_prefs.h" | |
| 25 #include "extensions/browser/extension_system.h" | 23 #include "extensions/browser/extension_system.h" |
| 26 #include "extensions/common/extension.h" | 24 #include "extensions/common/extension.h" |
| 27 #include "extensions/common/file_util.h" | 25 #include "extensions/common/file_util.h" |
| 28 #include "extensions/common/manifest.h" | 26 #include "extensions/common/manifest.h" |
| 29 | 27 |
| 30 using content::BrowserThread; | 28 using content::BrowserThread; |
| 31 | 29 |
| 32 namespace extensions { | 30 namespace extensions { |
| 33 namespace { | 31 namespace { |
| 34 | 32 |
| 35 // Path to shared extensions install dir. | 33 // A dictionary that maps shared extension IDs to version/paths/users. |
| 36 const char kSharedExtensionsDir[] = "/var/cache/shared_extensions"; | 34 const char kSharedExtensions[] = "SharedExtensions"; |
| 35 |
| 36 // Name of path attribute in shared extensions map. |
| 37 const char kSharedExtensionPath[] = "path"; |
| 38 |
| 39 // Name of users attribute (list of user emails) in shared extensions map. |
| 40 const char kSharedExtensionUsers[] = "users"; |
| 37 | 41 |
| 38 // Shared install dir overrider for tests only. | 42 // Shared install dir overrider for tests only. |
| 39 static const base::FilePath* g_shared_install_dir_override = NULL; | 43 static const base::FilePath* g_shared_install_dir_override = NULL; |
| 40 | 44 |
| 41 // This helper class lives on UI thread only. Main purpose of this class is to | 45 // This helper class lives on UI thread only. Main purpose of this class is to |
| 42 // track shared installation in progress between multiple profiles. | 46 // track shared installation in progress between multiple profiles. |
| 43 class ExtensionAssetsManagerHelper { | 47 class ExtensionAssetsManagerHelper { |
| 44 public: | 48 public: |
| 45 // Info about pending install request. | 49 // Info about pending install request. |
| 46 struct PendingInstallInfo { | 50 struct PendingInstallInfo { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 // Queue of pending installs in progress. | 106 // Queue of pending installs in progress. |
| 103 typedef std::map<InstallItem, std::vector<PendingInstallInfo> > InstallQueue; | 107 typedef std::map<InstallItem, std::vector<PendingInstallInfo> > InstallQueue; |
| 104 | 108 |
| 105 InstallQueue install_queue_; | 109 InstallQueue install_queue_; |
| 106 | 110 |
| 107 DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerHelper); | 111 DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerHelper); |
| 108 }; | 112 }; |
| 109 | 113 |
| 110 } // namespace | 114 } // namespace |
| 111 | 115 |
| 112 const char ExtensionAssetsManagerChromeOS::kSharedExtensions[] = | 116 // Path to shared extensions install dir. |
| 113 "SharedExtensions"; | 117 const char ExtensionAssetsManagerChromeOS::kSharedExtensionsDir[] = |
| 114 | 118 "/var/cache/shared_extensions"; |
| 115 const char ExtensionAssetsManagerChromeOS::kSharedExtensionPath[] = "path"; | |
| 116 | |
| 117 const char ExtensionAssetsManagerChromeOS::kSharedExtensionUsers[] = "users"; | |
| 118 | 119 |
| 119 ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() { } | 120 ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() { } |
| 120 | 121 |
| 121 ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() { | 122 ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() { |
| 122 if (g_shared_install_dir_override) { | 123 if (g_shared_install_dir_override) { |
| 123 delete g_shared_install_dir_override; | 124 delete g_shared_install_dir_override; |
| 124 g_shared_install_dir_override = NULL; | 125 g_shared_install_dir_override = NULL; |
| 125 } | 126 } |
| 126 } | 127 } |
| 127 | 128 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 // In some test extensions installed outside local_install_dir emulate | 176 // In some test extensions installed outside local_install_dir emulate |
| 176 // previous behavior that just do nothing in this case. | 177 // previous behavior that just do nothing in this case. |
| 177 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 178 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 178 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused, | 179 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused, |
| 179 id, | 180 id, |
| 180 profile)); | 181 profile)); |
| 181 } | 182 } |
| 182 } | 183 } |
| 183 | 184 |
| 184 // static | 185 // static |
| 185 base::FilePath ExtensionAssetsManagerChromeOS::GetSharedInstallDir() { | |
| 186 if (g_shared_install_dir_override) | |
| 187 return *g_shared_install_dir_override; | |
| 188 else | |
| 189 return base::FilePath(kSharedExtensionsDir); | |
| 190 } | |
| 191 | |
| 192 // static | |
| 193 bool ExtensionAssetsManagerChromeOS::CleanUpSharedExtensions( | |
| 194 std::multimap<std::string, base::FilePath>* live_extension_paths) { | |
| 195 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 196 | |
| 197 PrefService* local_state = g_browser_process->local_state(); | |
| 198 // It happens in many unit tests. | |
| 199 if (!local_state) | |
| 200 return false; | |
| 201 | |
| 202 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); | |
| 203 std::vector<std::string> extensions; | |
| 204 extensions.reserve(shared_extensions->size()); | |
| 205 for (base::DictionaryValue::Iterator it(*shared_extensions); | |
| 206 !it.IsAtEnd(); it.Advance()) { | |
| 207 extensions.push_back(it.key()); | |
| 208 } | |
| 209 | |
| 210 for (std::vector<std::string>::iterator it = extensions.begin(); | |
| 211 it != extensions.end(); it++) { | |
| 212 base::DictionaryValue* extension_info = NULL; | |
| 213 if (!shared_extensions->GetDictionary(*it, &extension_info)) { | |
| 214 NOTREACHED(); | |
| 215 return false; | |
| 216 } | |
| 217 if (!CleanUpExtension(*it, extension_info, live_extension_paths)) { | |
| 218 return false; | |
| 219 } | |
| 220 if (!extension_info->size()) | |
| 221 shared_extensions->RemoveWithoutPathExpansion(*it, NULL); | |
| 222 } | |
| 223 | |
| 224 return true; | |
| 225 } | |
| 226 | |
| 227 // static | |
| 228 void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting( | 186 void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting( |
| 229 const base::FilePath& install_dir) { | 187 const base::FilePath& install_dir) { |
| 230 DCHECK(!g_shared_install_dir_override); | 188 DCHECK(!g_shared_install_dir_override); |
| 231 g_shared_install_dir_override = new base::FilePath(install_dir); | 189 g_shared_install_dir_override = new base::FilePath(install_dir); |
| 232 } | 190 } |
| 233 | 191 |
| 234 // static | 192 // static |
| 235 base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner( | 193 base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner( |
| 236 Profile* profile) { | 194 Profile* profile) { |
| 237 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 195 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 238 ExtensionService* extension_service = | 196 ExtensionService* extension_service = |
| 239 ExtensionSystem::Get(profile)->extension_service(); | 197 ExtensionSystem::Get(profile)->extension_service(); |
| 240 return extension_service->GetFileTaskRunner(); | 198 return extension_service->GetFileTaskRunner(); |
| 241 } | 199 } |
| 242 | 200 |
| 243 // static | 201 // static |
| 202 base::FilePath ExtensionAssetsManagerChromeOS::GetSharedInstallDir() { |
| 203 if (g_shared_install_dir_override) |
| 204 return *g_shared_install_dir_override; |
| 205 else |
| 206 return base::FilePath(kSharedExtensionsDir); |
| 207 } |
| 208 |
| 209 // static |
| 244 bool ExtensionAssetsManagerChromeOS::CanShareAssets( | 210 bool ExtensionAssetsManagerChromeOS::CanShareAssets( |
| 245 const Extension* extension) { | 211 const Extension* extension) { |
| 246 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 212 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 247 chromeos::switches::kEnableExtensionAssetsSharing)) { | 213 chromeos::switches::kEnableExtensionAssetsSharing)) { |
| 248 return false; | 214 return false; |
| 249 } | 215 } |
| 250 | 216 |
| 251 // Chrome caches crx files for installed by default apps so sharing assets is | 217 // Chrome caches crx files for installed by default apps so sharing assets is |
| 252 // also possible. User specific apps should be excluded to not expose apps | 218 // also possible. User specific apps should be excluded to not expose apps |
| 253 // unique for the user outside of user's cryptohome. | 219 // unique for the user outside of user's cryptohome. |
| 254 return Manifest::IsExternalLocation(extension->location()); | 220 return Manifest::IsExternalLocation(extension->location()); |
| 255 } | 221 } |
| 256 | 222 |
| 257 // static | 223 // static |
| 258 void ExtensionAssetsManagerChromeOS::CheckSharedExtension( | 224 void ExtensionAssetsManagerChromeOS::CheckSharedExtension( |
| 259 const std::string& id, | 225 const std::string& id, |
| 260 const std::string& version, | 226 const std::string& version, |
| 261 const base::FilePath& unpacked_extension_root, | 227 const base::FilePath& unpacked_extension_root, |
| 262 const base::FilePath& local_install_dir, | 228 const base::FilePath& local_install_dir, |
| 263 Profile* profile, | 229 Profile* profile, |
| 264 InstallExtensionCallback callback) { | 230 InstallExtensionCallback callback) { |
| 265 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 231 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 266 | 232 |
| 267 const std::string& user_id = profile->GetProfileName(); | |
| 268 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); | |
| 269 if (!user_manager) { | |
| 270 NOTREACHED(); | |
| 271 return; | |
| 272 } | |
| 273 | |
| 274 if (user_manager->IsUserNonCryptohomeDataEphemeral(user_id) || | |
| 275 !user_manager->IsLoggedInAsRegularUser()) { | |
| 276 // Don't cache anything in shared location for ephemeral user or special | |
| 277 // user types. | |
| 278 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | |
| 279 FROM_HERE, | |
| 280 base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension, | |
| 281 id, | |
| 282 version, | |
| 283 unpacked_extension_root, | |
| 284 local_install_dir, | |
| 285 callback)); | |
| 286 return; | |
| 287 } | |
| 288 | |
| 289 PrefService* local_state = g_browser_process->local_state(); | 233 PrefService* local_state = g_browser_process->local_state(); |
| 290 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); | 234 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); |
| 291 base::DictionaryValue* extension_info = NULL; | 235 base::DictionaryValue* extension_info = NULL; |
| 292 base::DictionaryValue* version_info = NULL; | 236 base::DictionaryValue* version_info = NULL; |
| 293 base::ListValue* users = NULL; | 237 base::ListValue* users = NULL; |
| 294 std::string shared_path; | 238 std::string shared_path; |
| 295 if (shared_extensions->GetDictionary(id, &extension_info) && | 239 if (shared_extensions->GetDictionary(id, &extension_info) && |
| 296 extension_info->GetDictionaryWithoutPathExpansion( | 240 extension_info->GetDictionaryWithoutPathExpansion( |
| 297 version, &version_info) && | 241 version, &version_info) && |
| 298 version_info->GetString(kSharedExtensionPath, &shared_path) && | 242 version_info->GetString(kSharedExtensionPath, &shared_path) && |
| 299 version_info->GetList(kSharedExtensionUsers, &users)) { | 243 version_info->GetList(kSharedExtensionUsers, &users)) { |
| 300 // This extension version already in shared location. | 244 // This extension version already in shared location. |
| 245 const std::string& user_name = profile->GetProfileName(); |
| 301 size_t users_size = users->GetSize(); | 246 size_t users_size = users->GetSize(); |
| 302 bool user_found = false; | 247 bool user_found = false; |
| 303 for (size_t i = 0; i < users_size; i++) { | 248 for (size_t i = 0; i < users_size; i++) { |
| 304 std::string temp; | 249 std::string temp; |
| 305 if (users->GetString(i, &temp) && temp == user_id) { | 250 if (users->GetString(i, &temp) && temp == user_name) { |
| 306 // Re-installation for the same user. | 251 // Re-installation for the same user. |
| 307 user_found = true; | 252 user_found = true; |
| 308 break; | 253 break; |
| 309 } | 254 } |
| 310 } | 255 } |
| 311 if (!user_found) | 256 if (!user_found) |
| 312 users->AppendString(user_id); | 257 users->AppendString(user_name); |
| 313 | 258 |
| 314 // unpacked_extension_root will be deleted by CrxInstaller. | 259 // unpacked_extension_root will be deleted by CrxInstaller. |
| 315 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | 260 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( |
| 316 FROM_HERE, | 261 FROM_HERE, |
| 317 base::Bind(callback, base::FilePath(shared_path))); | 262 base::Bind(callback, base::FilePath(shared_path))); |
| 318 } else { | 263 } else { |
| 319 // Desired version is not found in shared location. | 264 // Desired version is not found in shared location. |
| 320 ExtensionAssetsManagerHelper* helper = | 265 ExtensionAssetsManagerHelper* helper = |
| 321 ExtensionAssetsManagerHelper::GetInstance(); | 266 ExtensionAssetsManagerHelper::GetInstance(); |
| 322 if (helper->RecordSharedInstall(id, version, unpacked_extension_root, | 267 if (helper->RecordSharedInstall(id, version, unpacked_extension_root, |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 454 NOTREACHED(); | 399 NOTREACHED(); |
| 455 continue; | 400 continue; |
| 456 } | 401 } |
| 457 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | 402 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( |
| 458 FROM_HERE, | 403 FROM_HERE, |
| 459 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion, | 404 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion, |
| 460 base::FilePath(shared_path))); | 405 base::FilePath(shared_path))); |
| 461 extension_info->RemoveWithoutPathExpansion(*it, NULL); | 406 extension_info->RemoveWithoutPathExpansion(*it, NULL); |
| 462 } | 407 } |
| 463 } | 408 } |
| 464 if (!extension_info->size()) { | 409 // Don't remove extension dir in shared location. It will be removed by GC |
| 465 shared_extensions->RemoveWithoutPathExpansion(id, NULL); | 410 // when it is safe to do so, and this avoids a race condition between |
| 466 // Don't remove extension dir in shared location. It will be removed by GC | 411 // concurrent uninstall by one user and install by another. |
| 467 // when it is safe to do so, and this avoids a race condition between | |
| 468 // concurrent uninstall by one user and install by another. | |
| 469 } | |
| 470 } | 412 } |
| 471 | 413 |
| 472 // static | 414 // static |
| 473 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion( | 415 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion( |
| 474 const base::FilePath& shared_version_dir) { | 416 const base::FilePath& shared_version_dir) { |
| 475 CHECK(GetSharedInstallDir().IsParent(shared_version_dir)); | 417 CHECK(GetSharedInstallDir().IsParent(shared_version_dir)); |
| 476 base::DeleteFile(shared_version_dir, true); // recursive. | 418 base::DeleteFile(shared_version_dir, true); // recursive. |
| 477 } | 419 } |
| 478 | 420 |
| 479 // static | |
| 480 bool ExtensionAssetsManagerChromeOS::CleanUpExtension( | |
| 481 const std::string& id, | |
| 482 base::DictionaryValue* extension_info, | |
| 483 std::multimap<std::string, base::FilePath>* live_extension_paths) { | |
| 484 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); | |
| 485 if (!user_manager) { | |
| 486 NOTREACHED(); | |
| 487 return false; | |
| 488 } | |
| 489 | |
| 490 std::vector<std::string> versions; | |
| 491 versions.reserve(extension_info->size()); | |
| 492 for (base::DictionaryValue::Iterator it(*extension_info); | |
| 493 !it.IsAtEnd(); it.Advance()) { | |
| 494 versions.push_back(it.key()); | |
| 495 } | |
| 496 | |
| 497 for (std::vector<std::string>::const_iterator it = versions.begin(); | |
| 498 it != versions.end(); it++) { | |
| 499 base::DictionaryValue* version_info = NULL; | |
| 500 base::ListValue* users = NULL; | |
| 501 std::string shared_path; | |
| 502 if (!extension_info->GetDictionaryWithoutPathExpansion(*it, | |
| 503 &version_info) || | |
| 504 !version_info->GetList(kSharedExtensionUsers, &users) || | |
| 505 !version_info->GetString(kSharedExtensionPath, &shared_path)) { | |
| 506 NOTREACHED(); | |
| 507 return false; | |
| 508 } | |
| 509 | |
| 510 size_t num_users = users->GetSize(); | |
| 511 for (size_t i = 0; i < num_users; i++) { | |
| 512 std::string user_id; | |
| 513 if (!users->GetString(i, &user_id)) { | |
| 514 NOTREACHED(); | |
| 515 return false; | |
| 516 } | |
| 517 const chromeos::User* user = user_manager->FindUser(user_id); | |
| 518 bool not_used = false; | |
| 519 if (!user) { | |
| 520 not_used = true; | |
| 521 } else if (user->is_logged_in()) { | |
| 522 // For logged in user also check that this path is actually used as | |
| 523 // installed extension or as delayed install. | |
| 524 Profile* profile = user_manager->GetProfileByUser(user); | |
| 525 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile); | |
| 526 if (!extension_prefs || extension_prefs->pref_service()->ReadOnly()) | |
| 527 return false; | |
| 528 | |
| 529 scoped_ptr<ExtensionInfo> info = | |
| 530 extension_prefs->GetInstalledExtensionInfo(id); | |
| 531 if (!info || info->extension_path != base::FilePath(shared_path)) { | |
| 532 info = extension_prefs->GetDelayedInstallInfo(id); | |
| 533 if (!info || info->extension_path != base::FilePath(shared_path)) { | |
| 534 not_used = true; | |
| 535 } | |
| 536 } | |
| 537 } | |
| 538 | |
| 539 if (not_used) { | |
| 540 users->Remove(i, NULL); | |
| 541 | |
| 542 i--; | |
| 543 num_users--; | |
| 544 } | |
| 545 } | |
| 546 | |
| 547 if (num_users) { | |
| 548 live_extension_paths->insert( | |
| 549 std::make_pair(id, base::FilePath(shared_path))); | |
| 550 } else { | |
| 551 extension_info->RemoveWithoutPathExpansion(*it, NULL); | |
| 552 } | |
| 553 } | |
| 554 | |
| 555 return true; | |
| 556 } | |
| 557 | |
| 558 } // namespace extensions | 421 } // namespace extensions |
| OLD | NEW |