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_factory.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
17 #include "chrome/common/url_constants.h"
18 #include "components/content_settings/core/browser/cookie_settings.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/manifest_handlers/app_isolation_info.h"
29 #include "extensions/common/manifest_handlers/content_capabilities_handler.h"
30 #include "extensions/common/permissions/permissions_data.h"
31 #include "storage/browser/quota/quota_manager.h"
32 #include "storage/common/quota/quota_status_code.h"
33 #include "storage/common/quota/quota_types.h"
35 using content::BrowserThread
;
36 using extensions::APIPermission
;
37 using extensions::Extension
;
38 using storage::SpecialStoragePolicy
;
42 void ReportQuotaUsage(storage::QuotaStatusCode code
, int64 usage
, int64 quota
) {
43 if (code
== storage::kQuotaStatusOk
) {
44 // We're interested in the amount of space hosted apps are using. Record it
45 // when the extension is granted the unlimited storage permission (once per
46 // extension load, so on average once per run).
47 UMA_HISTOGRAM_MEMORY_KB("Extensions.HostedAppUnlimitedStorageUsage", usage
);
51 // Log the usage for a hosted app with unlimited storage.
52 void LogHostedAppUnlimitedStorageUsage(
53 scoped_refptr
<const Extension
> extension
,
54 content::BrowserContext
* browser_context
) {
56 extensions::AppLaunchInfo::GetLaunchWebURL(extension
.get()).GetOrigin();
57 content::StoragePartition
* partition
=
58 browser_context
? // |browser_context| can be NULL in unittests.
59 content::BrowserContext::GetStoragePartitionForSite(browser_context
,
63 // We only have to query for kStorageTypePersistent data usage, because apps
64 // cannot ask for any more temporary storage, according to
65 // https://developers.google.com/chrome/whitepapers/storage.
66 BrowserThread::PostAfterStartupTask(
68 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
),
69 base::Bind(&storage::QuotaManager::GetUsageAndQuotaForWebApps
,
70 partition
->GetQuotaManager(),
72 storage::kStorageTypePersistent
,
73 base::Bind(&ReportQuotaUsage
)));
79 ExtensionSpecialStoragePolicy::ExtensionSpecialStoragePolicy(
80 content_settings::CookieSettings
* cookie_settings
)
81 : cookie_settings_(cookie_settings
) {
84 ExtensionSpecialStoragePolicy::~ExtensionSpecialStoragePolicy() {}
86 bool ExtensionSpecialStoragePolicy::IsStorageProtected(const GURL
& origin
) {
87 if (origin
.SchemeIs(extensions::kExtensionScheme
))
89 base::AutoLock
locker(lock_
);
90 return protected_apps_
.Contains(origin
);
93 bool ExtensionSpecialStoragePolicy::IsStorageUnlimited(const GURL
& origin
) {
94 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
95 switches::kUnlimitedStorage
))
98 if (origin
.SchemeIs(content::kChromeDevToolsScheme
) &&
99 origin
.host() == chrome::kChromeUIDevToolsHost
)
102 base::AutoLock
locker(lock_
);
103 return unlimited_extensions_
.Contains(origin
) ||
104 content_capabilities_unlimited_extensions_
.GrantsCapabilitiesTo(
108 bool ExtensionSpecialStoragePolicy::IsStorageSessionOnly(const GURL
& origin
) {
109 if (cookie_settings_
.get() == NULL
)
111 return cookie_settings_
->IsCookieSessionOnly(origin
);
114 bool ExtensionSpecialStoragePolicy::CanQueryDiskSize(const GURL
& origin
) {
115 base::AutoLock
locker(lock_
);
116 return installed_apps_
.Contains(origin
);
119 bool ExtensionSpecialStoragePolicy::HasSessionOnlyOrigins() {
120 if (cookie_settings_
.get() == NULL
)
122 if (cookie_settings_
->GetDefaultCookieSetting(NULL
) ==
123 CONTENT_SETTING_SESSION_ONLY
)
125 ContentSettingsForOneType entries
;
126 cookie_settings_
->GetCookieSettings(&entries
);
127 for (size_t i
= 0; i
< entries
.size(); ++i
) {
128 if (entries
[i
].setting
== CONTENT_SETTING_SESSION_ONLY
)
134 bool ExtensionSpecialStoragePolicy::HasIsolatedStorage(const GURL
& origin
) {
135 base::AutoLock
locker(lock_
);
136 return isolated_extensions_
.Contains(origin
);
139 bool ExtensionSpecialStoragePolicy::NeedsProtection(
140 const extensions::Extension
* extension
) {
141 return extension
->is_hosted_app() && !extension
->from_bookmark();
144 const extensions::ExtensionSet
*
145 ExtensionSpecialStoragePolicy::ExtensionsProtectingOrigin(
146 const GURL
& origin
) {
147 base::AutoLock
locker(lock_
);
148 return protected_apps_
.ExtensionsContaining(origin
);
151 void ExtensionSpecialStoragePolicy::GrantRightsForExtension(
152 const extensions::Extension
* extension
,
153 content::BrowserContext
* browser_context
) {
154 base::AutoLock
locker(lock_
);
157 int change_flags
= 0;
158 if (extensions::ContentCapabilitiesInfo::Get(extension
)
159 .permissions
.count(APIPermission::kUnlimitedStorage
) > 0) {
160 content_capabilities_unlimited_extensions_
.Add(extension
);
161 change_flags
|= SpecialStoragePolicy::STORAGE_UNLIMITED
;
164 if (NeedsProtection(extension
) ||
165 extension
->permissions_data()->HasAPIPermission(
166 APIPermission::kUnlimitedStorage
) ||
167 extension
->permissions_data()->HasAPIPermission(
168 APIPermission::kFileBrowserHandler
) ||
169 extensions::AppIsolationInfo::HasIsolatedStorage(extension
) ||
170 extension
->is_app()) {
171 if (NeedsProtection(extension
) && protected_apps_
.Add(extension
))
172 change_flags
|= SpecialStoragePolicy::STORAGE_PROTECTED
;
173 // FIXME: Does GrantRightsForExtension imply |extension| is installed?
174 if (extension
->is_app())
175 installed_apps_
.Add(extension
);
177 if (extension
->permissions_data()->HasAPIPermission(
178 APIPermission::kUnlimitedStorage
) &&
179 unlimited_extensions_
.Add(extension
)) {
180 if (extension
->is_hosted_app())
181 LogHostedAppUnlimitedStorageUsage(extension
, browser_context
);
182 change_flags
|= SpecialStoragePolicy::STORAGE_UNLIMITED
;
185 if (extension
->permissions_data()->HasAPIPermission(
186 APIPermission::kFileBrowserHandler
))
187 file_handler_extensions_
.Add(extension
);
189 if (extensions::AppIsolationInfo::HasIsolatedStorage(extension
))
190 isolated_extensions_
.Add(extension
);
194 NotifyGranted(Extension::GetBaseURLFromExtensionId(extension
->id()),
199 void ExtensionSpecialStoragePolicy::RevokeRightsForExtension(
200 const extensions::Extension
* extension
) {
201 base::AutoLock
locker(lock_
);
204 int change_flags
= 0;
205 if (extensions::ContentCapabilitiesInfo::Get(extension
)
206 .permissions
.count(APIPermission::kUnlimitedStorage
) > 0) {
207 content_capabilities_unlimited_extensions_
.Remove(extension
);
208 change_flags
|= SpecialStoragePolicy::STORAGE_UNLIMITED
;
211 if (NeedsProtection(extension
) ||
212 extension
->permissions_data()->HasAPIPermission(
213 APIPermission::kUnlimitedStorage
) ||
214 extension
->permissions_data()->HasAPIPermission(
215 APIPermission::kFileBrowserHandler
) ||
216 extensions::AppIsolationInfo::HasIsolatedStorage(extension
) ||
217 extension
->is_app()) {
218 if (NeedsProtection(extension
) && protected_apps_
.Remove(extension
))
219 change_flags
|= SpecialStoragePolicy::STORAGE_PROTECTED
;
221 if (extension
->is_app())
222 installed_apps_
.Remove(extension
);
224 if (extension
->permissions_data()->HasAPIPermission(
225 APIPermission::kUnlimitedStorage
) &&
226 unlimited_extensions_
.Remove(extension
))
227 change_flags
|= SpecialStoragePolicy::STORAGE_UNLIMITED
;
229 if (extension
->permissions_data()->HasAPIPermission(
230 APIPermission::kFileBrowserHandler
))
231 file_handler_extensions_
.Remove(extension
);
233 if (extensions::AppIsolationInfo::HasIsolatedStorage(extension
))
234 isolated_extensions_
.Remove(extension
);
238 NotifyRevoked(Extension::GetBaseURLFromExtensionId(extension
->id()),
243 void ExtensionSpecialStoragePolicy::RevokeRightsForAllExtensions() {
245 base::AutoLock
locker(lock_
);
246 protected_apps_
.Clear();
247 installed_apps_
.Clear();
248 unlimited_extensions_
.Clear();
249 file_handler_extensions_
.Clear();
250 isolated_extensions_
.Clear();
251 content_capabilities_unlimited_extensions_
.Clear();
257 void ExtensionSpecialStoragePolicy::NotifyGranted(
260 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
261 BrowserThread::PostTask(
262 BrowserThread::IO
, FROM_HERE
,
263 base::Bind(&ExtensionSpecialStoragePolicy::NotifyGranted
, this,
264 origin
, change_flags
));
267 SpecialStoragePolicy::NotifyGranted(origin
, change_flags
);
270 void ExtensionSpecialStoragePolicy::NotifyRevoked(
273 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
274 BrowserThread::PostTask(
275 BrowserThread::IO
, FROM_HERE
,
276 base::Bind(&ExtensionSpecialStoragePolicy::NotifyRevoked
, this,
277 origin
, change_flags
));
280 SpecialStoragePolicy::NotifyRevoked(origin
, change_flags
);
283 void ExtensionSpecialStoragePolicy::NotifyCleared() {
284 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
285 BrowserThread::PostTask(
286 BrowserThread::IO
, FROM_HERE
,
287 base::Bind(&ExtensionSpecialStoragePolicy::NotifyCleared
, this));
290 SpecialStoragePolicy::NotifyCleared();
293 //-----------------------------------------------------------------------------
294 // SpecialCollection helper class
295 //-----------------------------------------------------------------------------
297 ExtensionSpecialStoragePolicy::SpecialCollection::SpecialCollection() {}
299 ExtensionSpecialStoragePolicy::SpecialCollection::~SpecialCollection() {
300 STLDeleteValues(&cached_results_
);
303 bool ExtensionSpecialStoragePolicy::SpecialCollection::Contains(
304 const GURL
& origin
) {
305 return !ExtensionsContaining(origin
)->is_empty();
308 bool ExtensionSpecialStoragePolicy::SpecialCollection::GrantsCapabilitiesTo(
309 const GURL
& origin
) {
310 for (scoped_refptr
<const Extension
> extension
: extensions_
) {
311 if (extensions::ContentCapabilitiesInfo::Get(extension
.get())
312 .url_patterns
.MatchesURL(origin
)) {
319 const extensions::ExtensionSet
*
320 ExtensionSpecialStoragePolicy::SpecialCollection::ExtensionsContaining(
321 const GURL
& origin
) {
322 CachedResults::const_iterator found
= cached_results_
.find(origin
);
323 if (found
!= cached_results_
.end())
324 return found
->second
;
326 extensions::ExtensionSet
* result
= new extensions::ExtensionSet();
327 for (extensions::ExtensionSet::const_iterator iter
= extensions_
.begin();
328 iter
!= extensions_
.end(); ++iter
) {
329 if ((*iter
)->OverlapsWithOrigin(origin
))
330 result
->Insert(*iter
);
332 cached_results_
[origin
] = result
;
336 bool ExtensionSpecialStoragePolicy::SpecialCollection::ContainsExtension(
337 const std::string
& extension_id
) {
338 return extensions_
.Contains(extension_id
);
341 bool ExtensionSpecialStoragePolicy::SpecialCollection::Add(
342 const extensions::Extension
* extension
) {
344 return extensions_
.Insert(extension
);
347 bool ExtensionSpecialStoragePolicy::SpecialCollection::Remove(
348 const extensions::Extension
* extension
) {
350 return extensions_
.Remove(extension
->id());
353 void ExtensionSpecialStoragePolicy::SpecialCollection::Clear() {
358 void ExtensionSpecialStoragePolicy::SpecialCollection::ClearCache() {
359 STLDeleteValues(&cached_results_
);