OLD | NEW |
---|---|
(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 <vector> | |
8 | |
9 #include "base/command_line.h" | |
10 #include "base/file_util.h" | |
11 #include "base/memory/singleton.h" | |
12 #include "base/prefs/pref_registry_simple.h" | |
13 #include "base/prefs/pref_service.h" | |
14 #include "base/prefs/scoped_user_pref_update.h" | |
15 #include "base/sequenced_task_runner.h" | |
16 #include "base/sys_info.h" | |
17 #include "chrome/browser/browser_process.h" | |
18 #include "chrome/browser/extensions/extension_service.h" | |
19 #include "chrome/browser/profiles/profile.h" | |
20 #include "chromeos/chromeos_switches.h" | |
21 #include "content/public/browser/browser_thread.h" | |
22 #include "extensions/browser/extension_system.h" | |
23 #include "extensions/common/extension.h" | |
24 #include "extensions/common/file_util.h" | |
25 #include "extensions/common/manifest.h" | |
26 | |
27 using content::BrowserThread; | |
28 | |
29 namespace { | |
30 | |
31 // A dictionary that maps shared extension IDs to version/paths/users. | |
32 const char kSharedExtensions[] = "SharedExtensions"; | |
33 | |
34 // Name of path attribute in shared extensions map. | |
35 const char kSharedExtensionPath[] = "path"; | |
36 | |
37 // Name of users attribute (list of user emails) in shared extensions map. | |
38 const char kSharedExtensionUsers[] = "users"; | |
39 | |
40 } // namespace | |
41 | |
42 namespace extensions { | |
43 | |
44 // Path to shared extensions install dir. | |
45 const char ExtensionAssetsManagerChromeOS::kSharedExtensionsDir[] = | |
46 "/var/cache/shared_extensions"; | |
47 | |
48 ExtensionAssetsManagerChromeOS::ExtensionAssetsManagerChromeOS() | |
49 : weak_ptr_factory_(this), | |
50 shared_install_dir_(kSharedExtensionsDir) { | |
51 } | |
52 | |
53 ExtensionAssetsManagerChromeOS::~ExtensionAssetsManagerChromeOS() { | |
54 } | |
55 | |
56 // static | |
57 ExtensionAssetsManagerChromeOS* ExtensionAssetsManagerChromeOS::GetInstance() { | |
58 return Singleton<ExtensionAssetsManagerChromeOS>::get(); | |
59 } | |
60 | |
61 // static | |
62 void ExtensionAssetsManagerChromeOS::RegisterPrefs( | |
63 PrefRegistrySimple* registry) { | |
64 registry->RegisterDictionaryPref(kSharedExtensions); | |
65 } | |
66 | |
67 void ExtensionAssetsManagerChromeOS::InstallExtension( | |
68 const Extension* extension, | |
69 const base::FilePath& unpacked_extension_root, | |
70 const base::FilePath& local_install_dir, | |
71 Profile* profile, | |
72 InstallExtensionCallback callback) { | |
73 if (!CanShareAssets(extension)) { | |
74 ExtensionAssetsManager::InstallExtension( | |
75 extension, | |
76 unpacked_extension_root, | |
77 local_install_dir, | |
78 profile, | |
79 callback); | |
80 return; | |
81 } | |
82 | |
83 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
84 base::Bind(&ExtensionAssetsManagerChromeOS::CheckSharedExtension, | |
85 weak_ptr_factory_.GetWeakPtr(), | |
86 extension, | |
87 unpacked_extension_root, | |
88 local_install_dir, | |
89 profile, | |
90 callback)); | |
91 } | |
92 | |
93 void ExtensionAssetsManagerChromeOS::UninstallExtension( | |
94 const std::string& id, | |
95 Profile* profile, | |
96 const base::FilePath& local_install_dir, | |
97 const base::FilePath& extension_root) { | |
98 if (local_install_dir.IsParent(extension_root)) { | |
99 ExtensionAssetsManager::UninstallExtension( | |
100 id, profile, local_install_dir, extension_root); | |
101 return; | |
102 } | |
103 | |
104 DCHECK(shared_install_dir_.IsParent(extension_root)); | |
105 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
106 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused, | |
107 weak_ptr_factory_.GetWeakPtr(), | |
108 id, | |
109 profile)); | |
110 } | |
111 | |
112 void ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting( | |
113 const base::FilePath& install_dir) { | |
114 shared_install_dir_ = install_dir; | |
115 } | |
116 | |
117 // static | |
118 base::SequencedTaskRunner* ExtensionAssetsManagerChromeOS::GetFileTaskRunner( | |
119 Profile* profile) { | |
120 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
121 ExtensionService* extension_service = | |
122 ExtensionSystem::Get(profile)->extension_service(); | |
123 return extension_service->GetFileTaskRunner(); | |
124 } | |
125 | |
126 // static | |
127 bool ExtensionAssetsManagerChromeOS::CanShareAssets( | |
128 const Extension* extension) { | |
129 if (!CommandLine::ForCurrentProcess()->HasSwitch( | |
130 chromeos::switches::kEnableExtensionAssetsSharing)) { | |
131 return false; | |
132 } | |
133 | |
134 // Chrome caches crx files for installed by default apps so sharing assets is | |
135 // also possible. User specific apps should be excluded to don't expose apps | |
asargent_no_longer_on_chrome
2014/05/16 17:56:19
grammar nit: "should be excluded to don't" -> "sho
Dmitry Polukhin
2014/05/16 22:45:50
Done.
| |
136 // unique for the user outside of user's cryptohome. | |
137 return Manifest::IsExternalLocation(extension->location()); | |
138 } | |
139 | |
140 // static | |
141 void ExtensionAssetsManagerChromeOS::CheckSharedExtension( | |
142 base::WeakPtr<ExtensionAssetsManagerChromeOS> object, | |
143 const Extension* extension, | |
144 const base::FilePath& unpacked_extension_root, | |
145 const base::FilePath& local_install_dir, | |
146 Profile* profile, | |
147 InstallExtensionCallback callback) { | |
148 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
149 | |
150 PrefService* local_state = g_browser_process->local_state(); | |
151 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); | |
152 base::DictionaryValue* extension_info = NULL; | |
153 base::DictionaryValue* version_info = NULL; | |
154 base::ListValue* users = NULL; | |
155 std::string shared_path; | |
156 if (shared_extensions->GetDictionary(extension->id(), &extension_info) && | |
157 extension_info->GetDictionaryWithoutPathExpansion( | |
158 extension->VersionString(), &version_info) && | |
159 version_info->GetString(kSharedExtensionPath, &shared_path) && | |
160 version_info->GetList(kSharedExtensionUsers, &users)) { | |
161 // This extension version already in shared location. | |
162 const std::string& user_name = profile->GetProfileName(); | |
163 size_t users_size = users->GetSize(); | |
164 bool user_found = false; | |
165 for (size_t i = 0; i < users_size; i++) { | |
166 std::string temp; | |
167 if (users->GetString(i, &temp) && temp == user_name) { | |
168 // Re-installation for the same user. | |
169 user_found = true; | |
170 break; | |
171 } | |
172 } | |
173 if (!user_found) | |
174 users->AppendString(user_name); | |
175 | |
176 // unpacked_extension_root will be deleted by CrxInstaller. | |
177 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | |
178 FROM_HERE, | |
179 base::Bind(&ExtensionAssetsManagerChromeOS::SharedExtensionFound, | |
180 base::FilePath(shared_path), | |
181 callback)); | |
182 } else { | |
183 // Desired version not found in shared location. | |
184 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | |
185 FROM_HERE, | |
186 base::Bind(&ExtensionAssetsManagerChromeOS::SharedExtensionNotFound, | |
187 object, | |
188 extension, | |
189 unpacked_extension_root, | |
190 local_install_dir, | |
191 profile, | |
192 callback)); | |
193 } | |
194 } | |
195 | |
196 // static | |
197 void ExtensionAssetsManagerChromeOS::SharedExtensionFound( | |
198 const base::FilePath& shared_version_dir, | |
199 InstallExtensionCallback callback) { | |
200 callback.Run(shared_version_dir); | |
201 } | |
202 | |
203 // static | |
204 void ExtensionAssetsManagerChromeOS::SharedExtensionNotFound( | |
205 const Extension* extension, | |
206 const base::FilePath& unpacked_extension_root, | |
207 const base::FilePath& local_install_dir, | |
208 Profile* profile, | |
209 InstallExtensionCallback callback) { | |
210 base::FilePath shared_install_dir(shared_install_dir_); | |
211 base::FilePath shared_version_dir = file_util::InstallExtension( | |
212 unpacked_extension_root, | |
213 extension->id(), | |
214 extension->VersionString(), | |
215 shared_install_dir); | |
asargent_no_longer_on_chrome
2014/05/16 17:56:19
Will concurrent user sessions (eg a chromebox allo
Dmitry Polukhin
2014/05/16 22:45:50
Yes, in M37 we enabled multi-profile by default on
asargent_no_longer_on_chrome
2014/05/19 18:25:20
I spent a little time thinking about this and happ
| |
216 if (shared_version_dir.empty()) { | |
217 // Installation to shared location failed, try local dir. | |
218 // TODO(dpolukhin): add UMA stats reporting. | |
219 LOG(ERROR) << "Cannot install extension to shared location " | |
220 << shared_install_dir.value(); | |
221 callback.Run(file_util::InstallExtension( | |
222 unpacked_extension_root, | |
223 extension->id(), | |
224 extension->VersionString(), | |
225 local_install_dir)); | |
226 } else { | |
227 // Mark shared dir as used. | |
228 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
229 base::Bind(&ExtensionAssetsManagerChromeOS::MarkSharedExtensionUsed, | |
230 extension->id(), | |
231 extension->VersionString(), | |
232 shared_version_dir, | |
233 profile)); | |
234 | |
235 callback.Run(shared_version_dir); | |
236 } | |
237 } | |
238 | |
239 // static | |
240 void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUsed( | |
241 const std::string& id, | |
242 const std::string& version, | |
243 const base::FilePath& shared_version_dir, | |
244 Profile* profile) { | |
245 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
246 | |
247 PrefService* local_state = g_browser_process->local_state(); | |
248 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); | |
249 | |
250 base::DictionaryValue* extension_info = NULL; | |
251 if (!shared_extensions->GetDictionary(id, &extension_info)) { | |
252 extension_info = new base::DictionaryValue; | |
253 shared_extensions->Set(id, extension_info); | |
254 } | |
255 | |
256 base::DictionaryValue* version_info = NULL; | |
257 if (!extension_info->GetDictionaryWithoutPathExpansion(version, | |
258 &version_info)) { | |
259 version_info = new base::DictionaryValue; | |
260 extension_info->SetWithoutPathExpansion(version, version_info); | |
261 } | |
262 | |
263 CHECK(!version_info->HasKey(kSharedExtensionPath)); | |
264 version_info->SetString(kSharedExtensionPath, shared_version_dir.value()); | |
265 | |
266 CHECK(!version_info->HasKey(kSharedExtensionUsers)); | |
267 base::ListValue* users = new base::ListValue; | |
268 users->AppendString(profile->GetProfileName()); | |
269 version_info->Set(kSharedExtensionUsers, users); | |
270 } | |
271 | |
272 // static | |
273 void ExtensionAssetsManagerChromeOS::MarkSharedExtensionUnused( | |
274 base::WeakPtr<ExtensionAssetsManagerChromeOS> object, | |
275 const std::string& id, | |
276 Profile* profile) { | |
277 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
278 | |
279 PrefService* local_state = g_browser_process->local_state(); | |
280 DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions); | |
281 base::DictionaryValue* extension_info = NULL; | |
282 if (!shared_extensions->GetDictionary(id, &extension_info)) { | |
283 NOTREACHED(); | |
284 return; | |
285 } | |
286 | |
287 std::vector<std::string> versions; | |
288 versions.reserve(extension_info->size()); | |
289 for (base::DictionaryValue::Iterator it(*extension_info); | |
290 !it.IsAtEnd(); | |
291 it.Advance()) { | |
292 versions.push_back(it.key()); | |
293 } | |
294 | |
295 base::StringValue user_name(profile->GetProfileName()); | |
296 for (std::vector<std::string>::const_iterator it = versions.begin(); | |
297 it != versions.end(); it++) { | |
298 base::DictionaryValue* version_info = NULL; | |
299 if (!extension_info->GetDictionaryWithoutPathExpansion(*it, | |
300 &version_info)) { | |
301 NOTREACHED(); | |
302 continue; | |
303 } | |
304 base::ListValue* users = NULL; | |
305 if (!version_info->GetList(kSharedExtensionUsers, &users)) { | |
306 NOTREACHED(); | |
307 continue; | |
308 } | |
309 if (users->Remove(user_name, NULL) && !users->GetSize()) { | |
310 std::string shared_path; | |
311 if (!version_info->GetString(kSharedExtensionPath, &shared_path)) { | |
312 NOTREACHED(); | |
313 continue; | |
314 } | |
315 ExtensionAssetsManagerChromeOS::GetFileTaskRunner(profile)->PostTask( | |
316 FROM_HERE, | |
317 base::Bind(&ExtensionAssetsManagerChromeOS::DeleteSharedVersion, | |
318 object, | |
319 base::FilePath(shared_path))); | |
320 extension_info->RemoveWithoutPathExpansion(*it, NULL); | |
321 } | |
322 } | |
323 if (!extension_info->size()) | |
324 shared_extensions->RemoveWithoutPathExpansion(id, NULL); | |
325 } | |
326 | |
327 void ExtensionAssetsManagerChromeOS::DeleteSharedVersion( | |
328 const base::FilePath& shared_version_dir) { | |
329 CHECK(shared_install_dir_.IsParent(shared_version_dir)); | |
330 base::DeleteFile(shared_version_dir, true); // recursive. | |
331 } | |
332 | |
333 } // namespace extensions | |
OLD | NEW |