| 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" |
| 19 #include "chrome/browser/extensions/extension_service.h" | 20 #include "chrome/browser/extensions/extension_service.h" |
| 20 #include "chrome/browser/profiles/profile.h" | 21 #include "chrome/browser/profiles/profile.h" |
| 21 #include "chromeos/chromeos_switches.h" | 22 #include "chromeos/chromeos_switches.h" |
| 22 #include "content/public/browser/browser_thread.h" | 23 #include "content/public/browser/browser_thread.h" |
| 24 #include "extensions/browser/extension_prefs.h" |
| 23 #include "extensions/browser/extension_system.h" | 25 #include "extensions/browser/extension_system.h" |
| 24 #include "extensions/common/extension.h" | 26 #include "extensions/common/extension.h" |
| 25 #include "extensions/common/file_util.h" | 27 #include "extensions/common/file_util.h" |
| 26 #include "extensions/common/manifest.h" | 28 #include "extensions/common/manifest.h" |
| 27 | 29 |
| 28 using content::BrowserThread; | 30 using content::BrowserThread; |
| 29 | 31 |
| 30 namespace extensions { | 32 namespace extensions { |
| 31 namespace { | 33 namespace { |
| 32 | 34 |
| 33 // A dictionary that maps shared extension IDs to version/paths/users. | 35 // Path to shared extensions install dir. |
| 34 const char kSharedExtensions[] = "SharedExtensions"; | 36 const char kSharedExtensionsDir[] = "/var/cache/shared_extensions"; |
| 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"; | |
| 41 | 37 |
| 42 // Shared install dir overrider for tests only. | 38 // Shared install dir overrider for tests only. |
| 43 static const base::FilePath* g_shared_install_dir_override = NULL; | 39 static const base::FilePath* g_shared_install_dir_override = NULL; |
| 44 | 40 |
| 45 // This helper class lives on UI thread only. Main purpose of this class is to | 41 // This helper class lives on UI thread only. Main purpose of this class is to |
| 46 // track shared installation in progress between multiple profiles. | 42 // track shared installation in progress between multiple profiles. |
| 47 class ExtensionAssetsManagerHelper { | 43 class ExtensionAssetsManagerHelper { |
| 48 public: | 44 public: |
| 49 // Info about pending install request. | 45 // Info about pending install request. |
| 50 struct PendingInstallInfo { | 46 struct PendingInstallInfo { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 // Queue of pending installs in progress. | 102 // Queue of pending installs in progress. |
| 107 typedef std::map<InstallItem, std::vector<PendingInstallInfo> > InstallQueue; | 103 typedef std::map<InstallItem, std::vector<PendingInstallInfo> > InstallQueue; |
| 108 | 104 |
| 109 InstallQueue install_queue_; | 105 InstallQueue install_queue_; |
| 110 | 106 |
| 111 DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerHelper); | 107 DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerHelper); |
| 112 }; | 108 }; |
| 113 | 109 |
| 114 } // namespace | 110 } // namespace |
| 115 | 111 |
| 116 // Path to shared extensions install dir. | 112 const char ExtensionAssetsManagerChromeOS::kSharedExtensions[] = |
| 117 const char ExtensionAssetsManagerChromeOS::kSharedExtensionsDir[] = | 113 "SharedExtensions"; |
| 118 "/var/cache/shared_extensions"; | 114 |
| 115 const char ExtensionAssetsManagerChromeOS::kSharedExtensionPath[] = "path"; |
| 116 |
| 117 const char ExtensionAssetsManagerChromeOS::kSharedExtensionUsers[] = "users"; |
| 119 | 118 |
| 120 ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() { } | 119 ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() { } |
| 121 | 120 |
| 122 ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() { | 121 ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() { |
| 123 if (g_shared_install_dir_override) { | 122 if (g_shared_install_dir_override) { |
| 124 delete g_shared_install_dir_override; | 123 delete g_shared_install_dir_override; |
| 125 g_shared_install_dir_override = NULL; | 124 g_shared_install_dir_override = NULL; |
| 126 } | 125 } |
| 127 } | 126 } |
| 128 | 127 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 // In some test extensions installed outside local_install_dir emulate | 175 // In some test extensions installed outside local_install_dir emulate |
| 177 // previous behavior that just do nothing in this case. | 176 // previous behavior that just do nothing in this case. |
| 178 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 177 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 179 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused, | 178 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused, |
| 180 id, | 179 id, |
| 181 profile)); | 180 profile)); |
| 182 } | 181 } |
| 183 } | 182 } |
| 184 | 183 |
| 185 // static | 184 // 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 |
| 186 void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting( | 228 void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting( |
| 187 const base::FilePath& install_dir) { | 229 const base::FilePath& install_dir) { |
| 188 DCHECK(!g_shared_install_dir_override); | 230 DCHECK(!g_shared_install_dir_override); |
| 189 g_shared_install_dir_override = new base::FilePath(install_dir); | 231 g_shared_install_dir_override = new base::FilePath(install_dir); |
| 190 } | 232 } |
| 191 | 233 |
| 192 // static | 234 // static |
| 193 base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner( | 235 base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner( |
| 194 Profile* profile) { | 236 Profile* profile) { |
| 195 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 237 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 196 ExtensionService* extension_service = | 238 ExtensionService* extension_service = |
| 197 ExtensionSystem::Get(profile)->extension_service(); | 239 ExtensionSystem::Get(profile)->extension_service(); |
| 198 return extension_service->GetFileTaskRunner(); | 240 return extension_service->GetFileTaskRunner(); |
| 199 } | 241 } |
| 200 | 242 |
| 201 // static | 243 // 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 | |
| 210 bool ExtensionAssetsManagerChromeOS::CanShareAssets( | 244 bool ExtensionAssetsManagerChromeOS::CanShareAssets( |
| 211 const Extension* extension) { | 245 const Extension* extension) { |
| 212 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 246 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 213 chromeos::switches::kEnableExtensionAssetsSharing)) { | 247 chromeos::switches::kEnableExtensionAssetsSharing)) { |
| 214 return false; | 248 return false; |
| 215 } | 249 } |
| 216 | 250 |
| 217 // Chrome caches crx files for installed by default apps so sharing assets is | 251 // Chrome caches crx files for installed by default apps so sharing assets is |
| 218 // also possible. User specific apps should be excluded to not expose apps | 252 // also possible. User specific apps should be excluded to not expose apps |
| 219 // unique for the user outside of user's cryptohome. | 253 // unique for the user outside of user's cryptohome. |
| 220 return Manifest::IsExternalLocation(extension->location()); | 254 return Manifest::IsExternalLocation(extension->location()); |
| 221 } | 255 } |
| 222 | 256 |
| 223 // static | 257 // static |
| 224 void ExtensionAssetsManagerChromeOS::CheckSharedExtension( | 258 void ExtensionAssetsManagerChromeOS::CheckSharedExtension( |
| 225 const std::string& id, | 259 const std::string& id, |
| 226 const std::string& version, | 260 const std::string& version, |
| 227 const base::FilePath& unpacked_extension_root, | 261 const base::FilePath& unpacked_extension_root, |
| 228 const base::FilePath& local_install_dir, | 262 const base::FilePath& local_install_dir, |
| 229 Profile* profile, | 263 Profile* profile, |
| 230 InstallExtensionCallback callback) { | 264 InstallExtensionCallback callback) { |
| 231 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 265 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 232 | 266 |
| 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 |
| 233 PrefService* local_state = g_browser_process->local_state(); | 289 PrefService* local_state = g_browser_process->local_state(); |
| 234 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); | 290 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); |
| 235 base::DictionaryValue* extension_info = NULL; | 291 base::DictionaryValue* extension_info = NULL; |
| 236 base::DictionaryValue* version_info = NULL; | 292 base::DictionaryValue* version_info = NULL; |
| 237 base::ListValue* users = NULL; | 293 base::ListValue* users = NULL; |
| 238 std::string shared_path; | 294 std::string shared_path; |
| 239 if (shared_extensions->GetDictionary(id, &extension_info) && | 295 if (shared_extensions->GetDictionary(id, &extension_info) && |
| 240 extension_info->GetDictionaryWithoutPathExpansion( | 296 extension_info->GetDictionaryWithoutPathExpansion( |
| 241 version, &version_info) && | 297 version, &version_info) && |
| 242 version_info->GetString(kSharedExtensionPath, &shared_path) && | 298 version_info->GetString(kSharedExtensionPath, &shared_path) && |
| 243 version_info->GetList(kSharedExtensionUsers, &users)) { | 299 version_info->GetList(kSharedExtensionUsers, &users)) { |
| 244 // This extension version already in shared location. | 300 // This extension version already in shared location. |
| 245 const std::string& user_name = profile->GetProfileName(); | |
| 246 size_t users_size = users->GetSize(); | 301 size_t users_size = users->GetSize(); |
| 247 bool user_found = false; | 302 bool user_found = false; |
| 248 for (size_t i = 0; i < users_size; i++) { | 303 for (size_t i = 0; i < users_size; i++) { |
| 249 std::string temp; | 304 std::string temp; |
| 250 if (users->GetString(i, &temp) && temp == user_name) { | 305 if (users->GetString(i, &temp) && temp == user_id) { |
| 251 // Re-installation for the same user. | 306 // Re-installation for the same user. |
| 252 user_found = true; | 307 user_found = true; |
| 253 break; | 308 break; |
| 254 } | 309 } |
| 255 } | 310 } |
| 256 if (!user_found) | 311 if (!user_found) |
| 257 users->AppendString(user_name); | 312 users->AppendString(user_id); |
| 258 | 313 |
| 259 // unpacked_extension_root will be deleted by CrxInstaller. | 314 // unpacked_extension_root will be deleted by CrxInstaller. |
| 260 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | 315 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( |
| 261 FROM_HERE, | 316 FROM_HERE, |
| 262 base::Bind(callback, base::FilePath(shared_path))); | 317 base::Bind(callback, base::FilePath(shared_path))); |
| 263 } else { | 318 } else { |
| 264 // Desired version is not found in shared location. | 319 // Desired version is not found in shared location. |
| 265 ExtensionAssetsManagerHelper* helper = | 320 ExtensionAssetsManagerHelper* helper = |
| 266 ExtensionAssetsManagerHelper::GetInstance(); | 321 ExtensionAssetsManagerHelper::GetInstance(); |
| 267 if (helper->RecordSharedInstall(id, version, unpacked_extension_root, | 322 if (helper->RecordSharedInstall(id, version, unpacked_extension_root, |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 NOTREACHED(); | 454 NOTREACHED(); |
| 400 continue; | 455 continue; |
| 401 } | 456 } |
| 402 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | 457 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( |
| 403 FROM_HERE, | 458 FROM_HERE, |
| 404 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion, | 459 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion, |
| 405 base::FilePath(shared_path))); | 460 base::FilePath(shared_path))); |
| 406 extension_info->RemoveWithoutPathExpansion(*it, NULL); | 461 extension_info->RemoveWithoutPathExpansion(*it, NULL); |
| 407 } | 462 } |
| 408 } | 463 } |
| 409 // Don't remove extension dir in shared location. It will be removed by GC | 464 if (!extension_info->size()) { |
| 410 // when it is safe to do so, and this avoids a race condition between | 465 shared_extensions->RemoveWithoutPathExpansion(id, NULL); |
| 411 // concurrent uninstall by one user and install by another. | 466 // Don't remove extension dir in shared location. It will be removed by GC |
| 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 } |
| 412 } | 470 } |
| 413 | 471 |
| 414 // static | 472 // static |
| 415 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion( | 473 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion( |
| 416 const base::FilePath& shared_version_dir) { | 474 const base::FilePath& shared_version_dir) { |
| 417 CHECK(GetSharedInstallDir().IsParent(shared_version_dir)); | 475 CHECK(GetSharedInstallDir().IsParent(shared_version_dir)); |
| 418 base::DeleteFile(shared_version_dir, true); // recursive. | 476 base::DeleteFile(shared_version_dir, true); // recursive. |
| 419 } | 477 } |
| 420 | 478 |
| 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 |
| 421 } // namespace extensions | 558 } // namespace extensions |
| OLD | NEW |