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