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

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: comment fixed 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
(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 InstallLocalExtension(extension->id(),
148 extension->VersionString(),
149 unpacked_extension_root,
150 local_install_dir,
151 callback);
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();
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(callback, base::FilePath(shared_path)));
263 } else {
264 // Desired version is not found in shared location.
265 ExtensionAssetsManagerHelper* helper =
266 ExtensionAssetsManagerHelper::GetInstance();
267 if (helper->RecordSharedInstall(id, version, unpacked_extension_root,
268 local_install_dir, profile, callback)) {
269 // There is no install in progress for given <id, version> so run install.
270 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
271 FROM_HERE,
272 base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtension,
273 id,
274 version,
275 unpacked_extension_root));
276 }
277 }
278 }
279
280 // static
281 void ExtensionAssetsManagerChromeOS::InstallSharedExtension(
282 const std::string& id,
283 const std::string& version,
284 const base::FilePath& unpacked_extension_root) {
285 base::FilePath shared_install_dir = GetSharedInstallDir();
286 base::FilePath shared_version_dir = file_util::InstallExtension(
287 unpacked_extension_root, id, version, shared_install_dir);
288 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
289 base::Bind(&ExtensionAssetsManagerChromeOS::InstallSharedExtensionDone,
290 id, version, shared_version_dir));
291 }
292
293 // static
294 void ExtensionAssetsManagerChromeOS::InstallSharedExtensionDone(
295 const std::string& id,
296 const std::string& version,
297 const base::FilePath& shared_version_dir) {
298 DCHECK_CURRENTLY_ON(BrowserThread::UI);
299
300 ExtensionAssetsManagerHelper* helper =
301 ExtensionAssetsManagerHelper::GetInstance();
302 ExtensionAssetsManagerHelper::PendingInstallList pending_installs;
303 helper->SharedInstallDone(id, version, &pending_installs);
304
305 if (shared_version_dir.empty()) {
306 // Installation to shared location failed, try local dir.
307 // TODO(dpolukhin): add UMA stats reporting.
308 for (size_t i = 0; i < pending_installs.size(); i++) {
309 ExtensionAssetsManagerHelper::PendingInstallInfo& info =
310 pending_installs[i];
311 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
312 FROM_HERE,
313 base::Bind(&ExtensionAssetsManagerChromeOS::InstallLocalExtension,
314 id,
315 version,
316 info.unpacked_extension_root,
317 info.local_install_dir,
318 info.callback));
319 }
320 return;
321 }
322
323 PrefService* local_state = g_browser_process->local_state();
324 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
325 base::DictionaryValue* extension_info = NULL;
326 if (!shared_extensions->GetDictionary(id, &extension_info)) {
327 extension_info = new base::DictionaryValue;
328 shared_extensions->Set(id, extension_info);
329 }
330
331 CHECK(!shared_extensions->HasKey(version));
332 base::DictionaryValue* version_info = new base::DictionaryValue;
333 extension_info->SetWithoutPathExpansion(version, version_info);
334 version_info->SetString(kSharedExtensionPath, shared_version_dir.value());
335
336 base::ListValue* users = new base::ListValue;
337 version_info->Set(kSharedExtensionUsers, users);
338 for (size_t i = 0; i < pending_installs.size(); i++) {
339 ExtensionAssetsManagerHelper::PendingInstallInfo& info =
340 pending_installs[i];
341 users->AppendString(info.profile->GetProfileName());
342
343 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(info.profile)->PostTask(
344 FROM_HERE,
345 base::Bind(info.callback, shared_version_dir));
346 }
347 }
348
349 // static
350 void ExtensionAssetsManagerChromeOS::InstallLocalExtension(
351 const std::string& id,
352 const std::string& version,
353 const base::FilePath& unpacked_extension_root,
354 const base::FilePath& local_install_dir,
355 InstallExtensionCallback callback) {
356 callback.Run(file_util::InstallExtension(
357 unpacked_extension_root, id, version, local_install_dir));
358 }
359
360 // static
361 void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused(
362 const std::string& id,
363 Profile* profile) {
364 DCHECK_CURRENTLY_ON(BrowserThread::UI);
365
366 PrefService* local_state = g_browser_process->local_state();
367 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
368 base::DictionaryValue* extension_info = NULL;
369 if (!shared_extensions->GetDictionary(id, &extension_info)) {
370 NOTREACHED();
371 return;
372 }
373
374 std::vector<std::string> versions;
375 versions.reserve(extension_info->size());
376 for (base::DictionaryValue::Iterator it(*extension_info);
377 !it.IsAtEnd();
378 it.Advance()) {
379 versions.push_back(it.key());
380 }
381
382 base::StringValue user_name(profile->GetProfileName());
383 for (std::vector<std::string>::const_iterator it = versions.begin();
384 it != versions.end(); it++) {
385 base::DictionaryValue* version_info = NULL;
386 if (!extension_info->GetDictionaryWithoutPathExpansion(*it,
387 &version_info)) {
388 NOTREACHED();
389 continue;
390 }
391 base::ListValue* users = NULL;
392 if (!version_info->GetList(kSharedExtensionUsers, &users)) {
393 NOTREACHED();
394 continue;
395 }
396 if (users->Remove(user_name, NULL) && !users->GetSize()) {
397 std::string shared_path;
398 if (!version_info->GetString(kSharedExtensionPath, &shared_path)) {
399 NOTREACHED();
400 continue;
401 }
402 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask(
403 FROM_HERE,
404 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion,
405 base::FilePath(shared_path)));
406 extension_info->RemoveWithoutPathExpansion(*it, NULL);
407 }
408 }
409 // Don't remove extension dir in shared location. It will be removed by GC
410 // when it is safe to do so, and this avoids a race condition between
411 // concurrent uninstall by one user and install by another.
412 }
413
414 // static
415 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion(
416 const base::FilePath& shared_version_dir) {
417 CHECK(GetSharedInstallDir().IsParent(shared_version_dir));
418 base::DeleteFile(shared_version_dir, true); // recursive.
419 }
420
421 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_assets_manager_chromeos.h ('k') | chrome/browser/extensions/extension_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698