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

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

Issue 273193006: Install Chrome OS apps to shared location (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: added multi-profile support Created 6 years, 7 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
6
7 #include <map>
8 #include <vector>
9
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/memory/singleton.h"
13 #include "base/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/prefs/scoped_user_pref_update.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/sys_info.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chromeos/chromeos_switches.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/file_util.h"
26 #include "extensions/common/manifest.h"
27
28 using content::BrowserThread;
29
30 namespace extensions {
31 namespace {
32
33 // A dictionary that maps shared extension IDs to version/paths/users.
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";
41
42 // Shared install dir overrider for tests only.
43 static const base::FilePath* g_shared_install_dir_override = NULL;
44
45 // This helper class lives on UI thread only. Main purpose of this class is to
46 // track shared installation in progress between multiple profiles.
47 class ExtensionAssetsManagerHelper {
48 public:
49 // Info about pending install request.
50 struct PendingInstallInfo {
51 base::FilePath unpacked_extension_root;
52 base::FilePath local_install_dir;
53 Profile* profile;
54 ExtensionAssetsManager::InstallExtensionCallback callback;
55 };
56 typedef std::vector<PendingInstallInfo> PendingInstallList;
57
58 static ExtensionAssetsManagerHelper* GetInstance() {
59 DCHECK_CURRENTLY_ON(BrowserThread::UI);
60 return Singleton<ExtensionAssetsManagerHelper>::get();
61 }
62
63 // Remember that shared install is in progress. Return true if there is no
64 // other installs for given id and version.
65 bool RecordSharedInstall(
66 const std::string& id,
67 const std::string& version,
68 const base::FilePath& unpacked_extension_root,
69 const base::FilePath& local_install_dir,
70 Profile* profile,
71 ExtensionAssetsManager::InstallExtensionCallback callback) {
72 PendingInstallInfo install_info;
73 install_info.unpacked_extension_root = unpacked_extension_root;
74 install_info.local_install_dir = local_install_dir;
75 install_info.profile = profile;
76 install_info.callback = callback;
77
78 std::vector<PendingInstallInfo>& callbacks =
79 install_queue_[InstallQueue::key_type(id, version)];
80 callbacks.push_back(install_info);
81
82 return callbacks.size() == 1;
83 }
84
85 // Remove record about shared installation in progress and return
86 // |pending_installs|.
87 void SharedInstallDone(const std::string& id,
88 const std::string& version,
89 PendingInstallList* pending_installs) {
90 InstallQueue::iterator it = install_queue_.find(
91 InstallQueue::key_type(id, version));
92 DCHECK(it != install_queue_.end());
93 pending_installs->swap(it->second);
94 install_queue_.erase(it);
95 }
96
97 private:
98 friend struct DefaultSingletonTraits<ExtensionAssetsManagerHelper>;
99
100 ExtensionAssetsManagerHelper() {}
101 ~ExtensionAssetsManagerHelper() {}
102
103 // Extension ID + version pair.
104 typedef std::pair<std::string, std::string> InstallItem;
105
106 // Queue of pending installs in progress.
107 typedef std::map<InstallItem, std::vector<PendingInstallInfo> > InstallQueue;
108
109 InstallQueue install_queue_;
110
111 DISALLOW_COPY_AND_ASSIGN(ExtensionAssetsManagerHelper);
112 };
113
114 } // namespace
115
116 // Path to shared extensions install dir.
117 const char ExtensionAssetsManagerChromeOS::kSharedExtensionsDir[] =
118 "/var/cache/shared_extensions";
119
120 ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() { }
121
122 ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() {
123 if (g_shared_install_dir_override) {
124 delete g_shared_install_dir_override;
125 g_shared_install_dir_override = NULL;
126 }
127 }
128
129 // static
130 ExtensionAssetsManagerChromeOS* ExtensionAssetsManagerChromeOS::GetInstance() {
131 return Singleton<ExtensionAssetsManagerChromeOS>::get();
132 }
133
134 // static
135 void ExtensionAssetsManagerChromeOS::RegisterPrefs(
136 PrefRegistrySimple* registry) {
137 registry->RegisterDictionaryPref(kSharedExtensions);
138 }
139
140 void ExtensionAssetsManagerChromeOS::InstallExtension(
141 const Extension* extension,
142 const base::FilePath& unpacked_extension_root,
143 const base::FilePath& local_install_dir,
144 Profile* profile,
145 InstallExtensionCallback callback) {
146 if (!CanShareAssets(extension)) {
147 callback.Run(file_util::InstallExtension(
148 unpacked_extension_root,
149 extension->id(),
150 extension->VersionString(),
151 local_install_dir));
152 return;
153 }
154
155 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
156 base::Bind(&ExtensionAssetsManagerChromeOS::CheckSharedExtension,
157 extension->id(),
158 extension->VersionString(),
159 unpacked_extension_root,
160 local_install_dir,
161 profile,
162 callback));
163 }
164
165 void ExtensionAssetsManagerChromeOS::UninstallExtension(
166 const std::string& id,
167 Profile* profile,
168 const base::FilePath& local_install_dir,
169 const base::FilePath& extension_root) {
170 if (local_install_dir.IsParent(extension_root)) {
171 file_util::UninstallExtension(local_install_dir, id);
172 return;
173 }
174
175 if (GetSharedInstallDir().IsParent(extension_root)) {
176 // In some test extensions installed outside local_install_dir emulate
177 // previous behavior that just do nothing in this case.
178 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
179 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused,
180 id,
181 profile));
182 }
183 }
184
185 // static
186 void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting(
187 const base::FilePath& install_dir) {
188 DCHECK(!g_shared_install_dir_override);
189 g_shared_install_dir_override = new base::FilePath(install_dir);
190 }
191
192 // static
193 base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner(
194 Profile* profile) {
195 DCHECK_CURRENTLY_ON(BrowserThread::UI);
196 ExtensionService* extension_service =
197 ExtensionSystem::Get(profile)->extension_service();
198 return extension_service->GetFileTaskRunner();
199 }
200
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
210 bool ExtensionAssetsManagerChromeOS::CanShareAssets(
211 const Extension* extension) {
212 if (!CommandLine::ForCurrentProcess()->HasSwitch(
213 chromeos::switches::kEnableExtensionAssetsSharing)) {
214 return false;
215 }
216
217 // 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
219 // unique for the user outside of user's cryptohome.
220 return Manifest::IsExternalLocation(extension->location());
221 }
222
223 // static
224 void ExtensionAssetsManagerChromeOS::CheckSharedExtension(
225 const std::string& id,
226 const std::string& version,
227 const base::FilePath& unpacked_extension_root,
228 const base::FilePath& local_install_dir,
229 Profile* profile,
230 InstallExtensionCallback callback) {
231 DCHECK_CURRENTLY_ON(BrowserThread::UI);
232
233 PrefService* local_state = g_browser_process->local_state();
234 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
235 base::DictionaryValue* extension_info = NULL;
236 base::DictionaryValue* version_info = NULL;
237 base::ListValue* users = NULL;
238 std::string shared_path;
239 if (shared_extensions->GetDictionary(id, &extension_info) &&
240 extension_info->GetDictionaryWithoutPathExpansion(
241 version, &version_info) &&
242 version_info->GetString(kSharedExtensionPath, &shared_path) &&
243 version_info->GetList(kSharedExtensionUsers, &users)) {
244 // This extension version already in shared location.
245 const std::string& user_name = profile->GetProfileName();
asargent_no_longer_on_chrome 2014/05/20 21:20:25 I assume that GetProfileName() is guaranteed to be
Dmitry Polukhin 2014/05/20 23:42:16 On Chrome OS it is user email and Chrome OS distin
246 size_t users_size = users->GetSize();
247 bool user_found = false;
248 for (size_t i = 0; i < users_size; i++) {
249 std::string temp;
250 if (users->GetString(i, &temp) && temp == user_name) {
251 // Re-installation for the same user.
252 user_found = true;
253 break;
254 }
255 }
256 if (!user_found)
257 users->AppendString(user_name);
258
259 // unpacked_extension_root will be deleted by CrxInstaller.
260 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
261 FROM_HERE,
262 base::Bind(&ExtensionAssetsManagerChromeOS::RunInstallCallback,
263 base::FilePath(shared_path),
264 callback));
265 } else {
266 // Desired version is not found in shared location.
267 ExtensionAssetsManagerHelper* helper =
268 ExtensionAssetsManagerHelper::GetInstance();
269 if (helper->RecordSharedInstall(id, version, unpacked_extension_root,
270 local_install_dir, profile, callback)) {
271 // There is no install in progress for given <id, version> so run install.
272 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
273 FROM_HERE,
274 base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtension,
275 id,
276 version,
277 unpacked_extension_root));
278 }
279 }
280 }
281
282 // static
283 void ExtensionAssetsManagerChromeOS::RunInstallCallback(
284 const base::FilePath& shared_version_dir,
285 InstallExtensionCallback callback) {
286 callback.Run(shared_version_dir);
asargent_no_longer_on_chrome 2014/05/20 21:20:25 Since you don't do anything here but dispatch the
Dmitry Polukhin 2014/05/20 23:42:16 Yes, I used to have some extra thing but now it ca
287 }
288
289 // static
290 void ExtensionAssetsManagerChromeOS::InstallSharedExtension(
291 const std::string& id,
292 const std::string& version,
293 const base::FilePath& unpacked_extension_root) {
294 base::FilePath shared_install_dir = GetSharedInstallDir();
295 base::FilePath shared_version_dir = file_util::InstallExtension(
296 unpacked_extension_root, id, version, shared_install_dir);
297 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
298 base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtensionDone,
299 id, version, shared_version_dir));
300 }
301
302 // static
303 void ExtensionAssetsManagerChromeOS::InstallSharedExtensionDone(
304 const std::string& id,
305 const std::string& version,
306 const base::FilePath& shared_version_dir) {
307 DCHECK_CURRENTLY_ON(BrowserThread::UI);
308
309 ExtensionAssetsManagerHelper* helper =
310 ExtensionAssetsManagerHelper::GetInstance();
311 ExtensionAssetsManagerHelper::PendingInstallList pending_installs;
312 helper->SharedInstallDone(id, version, &pending_installs);
313
314 PrefService* local_state = g_browser_process->local_state();
315 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
316 base::DictionaryValue* extension_info = NULL;
317 base::DictionaryValue* version_info = NULL;
318 base::ListValue* users = NULL;
319
320 for (size_t i = 0; i < pending_installs.size(); i++) {
321 ExtensionAssetsManagerHelper::PendingInstallInfo& info =
322 pending_installs[i];
323 if (shared_version_dir.empty()) {
324 // Installation to shared location failed, try local dir.
325 // TODO(dpolukhin): add UMA stats reporting.
326 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
327 FROM_HERE,
328 base::Bind(&ExtensionAssetsManagerChromeOS::SharedInstallFailed,
329 id,
330 version,
331 info.unpacked_extension_root,
332 info.local_install_dir,
333 info.callback));
334 } else {
335 if (!extension_info &&
asargent_no_longer_on_chrome 2014/05/20 21:20:25 It looks like extension_info gets initialized to N
Dmitry Polukhin 2014/05/20 23:42:16 If I move declaration to for-loop it will be re-in
336 !shared_extensions->GetDictionary(id, &extension_info)) {
337 extension_info = new base::DictionaryValue;
338 shared_extensions->Set(id, extension_info);
339 }
340 if (!version_info &&
asargent_no_longer_on_chrome 2014/05/20 21:20:25 similar comment about version_info
Dmitry Polukhin 2014/05/20 23:42:16 Done.
341 !extension_info->GetDictionaryWithoutPathExpansion(version,
342 &version_info)) {
343 version_info = new base::DictionaryValue;
344 extension_info->SetWithoutPathExpansion(version, version_info);
345
346 CHECK(!version_info->HasKey(kSharedExtensionPath));
347 CHECK(!version_info->HasKey(kSharedExtensionUsers));
348 version_info->SetString(kSharedExtensionPath,
349 shared_version_dir.value());
350 }
351 if (!users) {
352 users = new base::ListValue;
353 version_info->Set(kSharedExtensionUsers, users);
354 }
355 users->AppendString(info.profile->GetProfileName());
356
357 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
358 FROM_HERE,
359 base::Bind(&ExtensionAssetsManagerChromeOS::RunInstallCallback,
360 shared_version_dir,
361 info.callback));
362 }
363 }
364 }
365
366 // static
367 void ExtensionAssetsManagerChromeOS::SharedInstallFailed(
368 const std::string& id,
369 const std::string& version,
370 const base::FilePath& unpacked_extension_root,
371 const base::FilePath& local_install_dir,
372 InstallExtensionCallback callback) {
373 callback.Run(file_util::InstallExtension(
374 unpacked_extension_root, id, version, local_install_dir));
375 }
376
377 // static
378 void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused(
379 const std::string& id,
380 Profile* profile) {
381 DCHECK_CURRENTLY_ON(BrowserThread::UI);
382
383 PrefService* local_state = g_browser_process->local_state();
384 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
385 base::DictionaryValue* extension_info = NULL;
386 if (!shared_extensions->GetDictionary(id, &extension_info)) {
387 NOTREACHED();
388 return;
389 }
390
391 std::vector<std::string> versions;
392 versions.reserve(extension_info->size());
393 for (base::DictionaryValue::Iterator it(*extension_info);
394 !it.IsAtEnd();
395 it.Advance()) {
396 versions.push_back(it.key());
397 }
398
399 base::StringValue user_name(profile->GetProfileName());
400 for (std::vector<std::string>::const_iterator it = versions.begin();
401 it != versions.end(); it++) {
402 base::DictionaryValue* version_info = NULL;
403 if (!extension_info->GetDictionaryWithoutPathExpansion(*it,
404 &version_info)) {
405 NOTREACHED();
406 continue;
407 }
408 base::ListValue* users = NULL;
409 if (!version_info->GetList(kSharedExtensionUsers, &users)) {
410 NOTREACHED();
411 continue;
412 }
413 if (users->Remove(user_name, NULL) && !users->GetSize()) {
414 std::string shared_path;
415 if (!version_info->GetString(kSharedExtensionPath, &shared_path)) {
416 NOTREACHED();
417 continue;
418 }
419 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
420 FROM_HERE,
421 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion,
422 base::FilePath(shared_path)));
423 extension_info->RemoveWithoutPathExpansion(*it, NULL);
424 }
425 }
426 if (!extension_info->size()) {
427 shared_extensions->RemoveWithoutPathExpansion(id, NULL);
428 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
429 FROM_HERE,
430 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedExtension, id));
asargent_no_longer_on_chrome 2014/05/20 21:20:25 Imagine this scenario: -We have a version of exte
Dmitry Polukhin 2014/05/20 23:42:16 Yep, we do have race here even between removing gi
Dmitry Polukhin 2014/05/21 00:37:46 I thought more about it and it seems that this rac
431 }
432 }
433
434 // static
435 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion(
436 const base::FilePath& shared_version_dir) {
437 CHECK(GetSharedInstallDir().IsParent(shared_version_dir));
438 base::DeleteFile(shared_version_dir, true); // recursive.
439 }
440
441 // static
442 void ExtensionAssetsManagerChromeOS::DeleteSharedExtension(
443 const std::string& id) {
444 file_util::UninstallExtension(GetSharedInstallDir(), id);
445 }
446
447 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698