Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: chrome/browser/extensions/extension_assets_manager_chromeos.cc

Issue 303693011: Add garbage collection for shared extensions on Chrome OS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698