1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/extension_special_storage_policy.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/content_settings/cookie_settings.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
17 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
18 #include "chrome/common/url_constants.h"
19 #include "components/content_settings/core/common/content_settings.h"
20 #include "components/content_settings/core/common/content_settings_types.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/storage_partition.h"
24 #include "content/public/common/url_constants.h"
25 #include "extensions/common/constants.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_set.h"
28 #include "extensions/common/permissions/permissions_data.h"
29 #include "storage/browser/quota/quota_manager.h"
30 #include "storage/common/quota/quota_status_code.h"
31 #include "storage/common/quota/quota_types.h"
33 using content::BrowserThread
;
34 using extensions::APIPermission
;
35 using extensions::Extension
;
36 using storage::SpecialStoragePolicy
;
40 void ReportQuotaUsage(storage::QuotaStatusCode code
, int64 usage
, int64 quota
) {
41 if (code
== storage::kQuotaStatusOk
) {
42 // We're interested in the amount of space hosted apps are using. Record it
43 // when the extension is granted the unlimited storage permission (once per
44 // extension load, so on average once per run).
45 UMA_HISTOGRAM_MEMORY_KB("Extensions.HostedAppUnlimitedStorageUsage", usage
);
49 // Log the usage for a hosted app with unlimited storage.
50 void LogHostedAppUnlimitedStorageUsage(
51 scoped_refptr
<const Extension
> extension
,
52 content::BrowserContext
* browser_context
) {
54 extensions::AppLaunchInfo::GetLaunchWebURL(extension
.get()).GetOrigin();
55 content::StoragePartition
* partition
=
56 browser_context
? // |browser_context| can be NULL in unittests.
57 content::BrowserContext::GetStoragePartitionForSite(browser_context
,
61 // We only have to query for kStorageTypePersistent data usage, because apps
62 // cannot ask for any more temporary storage, according to
63 // https://developers.google.com/chrome/whitepapers/storage.
64 BrowserThread::PostTask(
67 base::Bind(&storage::QuotaManager::GetUsageAndQuotaForWebApps
,
68 partition
->GetQuotaManager(),
70 storage::kStorageTypePersistent
,
71 base::Bind(&ReportQuotaUsage
)));
77 ExtensionSpecialStoragePolicy::ExtensionSpecialStoragePolicy(
78 CookieSettings
* cookie_settings
)
79 : cookie_settings_(cookie_settings
) {}
81 ExtensionSpecialStoragePolicy::~ExtensionSpecialStoragePolicy() {}
83 bool ExtensionSpecialStoragePolicy::IsStorageProtected(const GURL
& origin
) {
84 if (origin
.SchemeIs(extensions::kExtensionScheme
))
86 base::AutoLock
locker(lock_
);
87 return protected_apps_
.Contains(origin
);
90 bool ExtensionSpecialStoragePolicy::IsStorageUnlimited(const GURL
& origin
) {
91 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUnlimitedStorage
))
94 if (origin
.SchemeIs(content::kChromeDevToolsScheme
) &&
95 origin
.host() == chrome::kChromeUIDevToolsHost
)
98 base::AutoLock
locker(lock_
);
99 return unlimited_extensions_
.Contains(origin
);
102 bool ExtensionSpecialStoragePolicy::IsStorageSessionOnly(const GURL
& origin
) {
103 if (cookie_settings_
.get() == NULL
)
105 return cookie_settings_
->IsCookieSessionOnly(origin
);
108 bool ExtensionSpecialStoragePolicy::CanQueryDiskSize(const GURL
& origin
) {
109 return installed_apps_
.Contains(origin
);
112 bool ExtensionSpecialStoragePolicy::HasSessionOnlyOrigins() {
113 if (cookie_settings_
.get() == NULL
)
115 if (cookie_settings_
->GetDefaultCookieSetting(NULL
) ==
116 CONTENT_SETTING_SESSION_ONLY
)
118 ContentSettingsForOneType entries
;
119 cookie_settings_
->GetCookieSettings(&entries
);
120 for (size_t i
= 0; i
< entries
.size(); ++i
) {
121 if (entries
[i
].setting
== CONTENT_SETTING_SESSION_ONLY
)
127 bool ExtensionSpecialStoragePolicy::IsFileHandler(
128 const std::string
& extension_id
) {
129 base::AutoLock
locker(lock_
);
130 return file_handler_extensions_
.ContainsExtension(extension_id
);
133 bool ExtensionSpecialStoragePolicy::HasIsolatedStorage(const GURL
& origin
) {
134 base::AutoLock
locker(lock_
);
135 return isolated_extensions_
.Contains(origin
);
138 bool ExtensionSpecialStoragePolicy::NeedsProtection(
139 const extensions::Extension
* extension
) {
140 return extension
->is_hosted_app() && !extension
->from_bookmark();
143 const extensions::ExtensionSet
*
144 ExtensionSpecialStoragePolicy::ExtensionsProtectingOrigin(
145 const GURL
& origin
) {
146 base::AutoLock
locker(lock_
);
147 return protected_apps_
.ExtensionsContaining(origin
);
150 void ExtensionSpecialStoragePolicy::GrantRightsForExtension(
151 const extensions::Extension
* extension
,
152 content::BrowserContext
* browser_context
) {
154 if (!(NeedsProtection(extension
) ||
155 extension
->permissions_data()->HasAPIPermission(
156 APIPermission::kUnlimitedStorage
) ||
157 extension
->permissions_data()->HasAPIPermission(
158 APIPermission::kFileBrowserHandler
) ||
159 extensions::AppIsolationInfo::HasIsolatedStorage(extension
) ||
160 extension
->is_app())) {
164 int change_flags
= 0;
166 base::AutoLock
locker(lock_
);
167 if (NeedsProtection(extension
) && protected_apps_
.Add(extension
))
168 change_flags
|= SpecialStoragePolicy::STORAGE_PROTECTED
;
169 // FIXME: Does GrantRightsForExtension imply |extension| is installed?
170 if (extension
->is_app())
171 installed_apps_
.Add(extension
);
173 if (extension
->permissions_data()->HasAPIPermission(
174 APIPermission::kUnlimitedStorage
) &&
175 unlimited_extensions_
.Add(extension
)) {
176 if (extension
->is_hosted_app())
177 LogHostedAppUnlimitedStorageUsage(extension
, browser_context
);
179 change_flags
|= SpecialStoragePolicy::STORAGE_UNLIMITED
;
182 if (extension
->permissions_data()->HasAPIPermission(
183 APIPermission::kFileBrowserHandler
))
184 file_handler_extensions_
.Add(extension
);
186 if (extensions::AppIsolationInfo::HasIsolatedStorage(extension
))
187 isolated_extensions_
.Add(extension
);
191 NotifyGranted(Extension::GetBaseURLFromExtensionId(extension
->id()),
196 void ExtensionSpecialStoragePolicy::RevokeRightsForExtension(
197 const extensions::Extension
* extension
) {
199 if (!(NeedsProtection(extension
) ||
200 extension
->permissions_data()->HasAPIPermission(
201 APIPermission::kUnlimitedStorage
) ||
202 extension
->permissions_data()->HasAPIPermission(
203 APIPermission::kFileBrowserHandler
) ||
204 extensions::AppIsolationInfo::HasIsolatedStorage(extension
) ||
205 extension
->is_app())) {
208 int change_flags
= 0;
210 base::AutoLock
locker(lock_
);
211 if (NeedsProtection(extension
) && protected_apps_
.Remove(extension
))
212 change_flags
|= SpecialStoragePolicy::STORAGE_PROTECTED
;
214 if (extension
->is_app())
215 installed_apps_
.Remove(extension
);
217 if (extension
->permissions_data()->HasAPIPermission(
218 APIPermission::kUnlimitedStorage
) &&
219 unlimited_extensions_
.Remove(extension
))
220 change_flags
|= SpecialStoragePolicy::STORAGE_UNLIMITED
;
222 if (extension
->permissions_data()->HasAPIPermission(
223 APIPermission::kFileBrowserHandler
))
224 file_handler_extensions_
.Remove(extension
);
226 if (extensions::AppIsolationInfo::HasIsolatedStorage(extension
))
227 isolated_extensions_
.Remove(extension
);
231 NotifyRevoked(Extension::GetBaseURLFromExtensionId(extension
->id()),
236 void ExtensionSpecialStoragePolicy::RevokeRightsForAllExtensions() {
238 base::AutoLock
locker(lock_
);
239 protected_apps_
.Clear();
240 installed_apps_
.Clear();
241 unlimited_extensions_
.Clear();
242 file_handler_extensions_
.Clear();
243 isolated_extensions_
.Clear();
249 void ExtensionSpecialStoragePolicy::NotifyGranted(
252 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
253 BrowserThread::PostTask(
254 BrowserThread::IO
, FROM_HERE
,
255 base::Bind(&ExtensionSpecialStoragePolicy::NotifyGranted
, this,
256 origin
, change_flags
));
259 SpecialStoragePolicy::NotifyGranted(origin
, change_flags
);
262 void ExtensionSpecialStoragePolicy::NotifyRevoked(
265 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
266 BrowserThread::PostTask(
267 BrowserThread::IO
, FROM_HERE
,
268 base::Bind(&ExtensionSpecialStoragePolicy::NotifyRevoked
, this,
269 origin
, change_flags
));
272 SpecialStoragePolicy::NotifyRevoked(origin
, change_flags
);
275 void ExtensionSpecialStoragePolicy::NotifyCleared() {
276 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
277 BrowserThread::PostTask(
278 BrowserThread::IO
, FROM_HERE
,
279 base::Bind(&ExtensionSpecialStoragePolicy::NotifyCleared
, this));
282 SpecialStoragePolicy::NotifyCleared();
285 //-----------------------------------------------------------------------------
286 // SpecialCollection helper class
287 //-----------------------------------------------------------------------------
289 ExtensionSpecialStoragePolicy::SpecialCollection::SpecialCollection() {}
291 ExtensionSpecialStoragePolicy::SpecialCollection::~SpecialCollection() {
292 STLDeleteValues(&cached_results_
);
295 bool ExtensionSpecialStoragePolicy::SpecialCollection::Contains(
296 const GURL
& origin
) {
297 return !ExtensionsContaining(origin
)->is_empty();
300 const extensions::ExtensionSet
*
301 ExtensionSpecialStoragePolicy::SpecialCollection::ExtensionsContaining(
302 const GURL
& origin
) {
303 CachedResults::const_iterator found
= cached_results_
.find(origin
);
304 if (found
!= cached_results_
.end())
305 return found
->second
;
307 extensions::ExtensionSet
* result
= new extensions::ExtensionSet();
308 for (extensions::ExtensionSet::const_iterator iter
= extensions_
.begin();
309 iter
!= extensions_
.end(); ++iter
) {
310 if ((*iter
)->OverlapsWithOrigin(origin
))
311 result
->Insert(*iter
);
313 cached_results_
[origin
] = result
;
317 bool ExtensionSpecialStoragePolicy::SpecialCollection::ContainsExtension(
318 const std::string
& extension_id
) {
319 return extensions_
.Contains(extension_id
);
322 bool ExtensionSpecialStoragePolicy::SpecialCollection::Add(
323 const extensions::Extension
* extension
) {
325 return extensions_
.Insert(extension
);
328 bool ExtensionSpecialStoragePolicy::SpecialCollection::Remove(
329 const extensions::Extension
* extension
) {
331 return extensions_
.Remove(extension
->id());
334 void ExtensionSpecialStoragePolicy::SpecialCollection::Clear() {
339 void ExtensionSpecialStoragePolicy::SpecialCollection::ClearCache() {
340 STLDeleteValues(&cached_results_
);
341 cached_results_
.clear();