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 |