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 |