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

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

Issue 314563003: Handle promotion of ephemeral apps in the ExtensionStorageMonitor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nit in comment 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/extension_storage_monitor.h" 5 #include "chrome/browser/extensions/extension_storage_monitor.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h" 10 #include "base/strings/string_util.h"
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 // threshold than installed extensions and apps. Once a threshold is exceeded, 47 // threshold than installed extensions and apps. Once a threshold is exceeded,
48 // it will be doubled to throttle notifications. 48 // it will be doubled to throttle notifications.
49 const int64 kMBytes = 1024 * 1024; 49 const int64 kMBytes = 1024 * 1024;
50 const int64 kEphemeralAppInitialThreshold = 250 * kMBytes; 50 const int64 kEphemeralAppInitialThreshold = 250 * kMBytes;
51 const int64 kExtensionInitialThreshold = 1000 * kMBytes; 51 const int64 kExtensionInitialThreshold = 1000 * kMBytes;
52 52
53 // Notifications have an ID so that we can update them. 53 // Notifications have an ID so that we can update them.
54 const char kNotificationIdFormat[] = "ExtensionStorageMonitor-$1-$2"; 54 const char kNotificationIdFormat[] = "ExtensionStorageMonitor-$1-$2";
55 const char kSystemNotifierId[] = "ExtensionStorageMonitor"; 55 const char kSystemNotifierId[] = "ExtensionStorageMonitor";
56 56
57 // A preference that stores the next threshold for displaying a notification
58 // when an extension or app consumes excessive disk space. This will not be
59 // set until the extension/app reaches the initial threshold.
60 const char kPrefNextStorageThreshold[] = "next_storage_threshold";
61
62 // If this preference is set to true, notifications will be suppressed when an
63 // extension or app consumes excessive disk space.
64 const char kPrefDisableStorageNotifications[] = "disable_storage_notifications";
65
66 bool ShouldMonitorStorageFor(const Extension* extension) {
67 // Only monitor storage for extensions that are granted unlimited storage.
68 // Do not monitor storage for component extensions.
69 return extension->HasAPIPermission(APIPermission::kUnlimitedStorage) &&
70 extension->location() != Manifest::COMPONENT;
71 }
72
57 } // namespace 73 } // namespace
58 74
59 // StorageEventObserver monitors the storage usage of extensions and lives on 75 // StorageEventObserver monitors the storage usage of extensions and lives on
60 // the IO thread. When a threshold is exceeded, a message will be posted to the 76 // the IO thread. When a threshold is exceeded, a message will be posted to the
61 // UI thread, which displays the notification. 77 // UI thread, which displays the notification.
62 class StorageEventObserver 78 class StorageEventObserver
63 : public base::RefCountedThreadSafe< 79 : public base::RefCountedThreadSafe<
64 StorageEventObserver, 80 StorageEventObserver,
65 BrowserThread::DeleteOnIOThread>, 81 BrowserThread::DeleteOnIOThread>,
66 public quota::StorageObserver { 82 public quota::StorageObserver {
(...skipping 20 matching lines...) Expand all
87 state.next_threshold = next_threshold; 103 state.next_threshold = next_threshold;
88 104
89 quota::StorageObserver::MonitorParams params( 105 quota::StorageObserver::MonitorParams params(
90 kMonitorStorageType, 106 kMonitorStorageType,
91 origin, 107 origin,
92 base::TimeDelta::FromSeconds(rate), 108 base::TimeDelta::FromSeconds(rate),
93 false); 109 false);
94 quota_manager->AddStorageObserver(this, params); 110 quota_manager->AddStorageObserver(this, params);
95 } 111 }
96 112
113 // Updates the threshold for an extension already being monitored.
114 void UpdateThresholdForExtension(const std::string& extension_id,
115 int64 next_threshold) {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117
118 for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
119 it != origin_state_map_.end();
120 ++it) {
121 if (it->second.extension_id == extension_id) {
122 it->second.next_threshold = next_threshold;
123 break;
124 }
125 }
126 }
127
97 // Deregister as an observer for the extension's storage events. 128 // Deregister as an observer for the extension's storage events.
98 void StopObservingForExtension(const std::string& extension_id) { 129 void StopObservingForExtension(const std::string& extension_id) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
100 131
101 for (OriginStorageStateMap::iterator it = origin_state_map_.begin(); 132 for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
102 it != origin_state_map_.end(); ) { 133 it != origin_state_map_.end(); ) {
103 if (it->second.extension_id == extension_id) { 134 if (it->second.extension_id == extension_id) {
104 quota::StorageObserver::Filter filter(kMonitorStorageType, it->first); 135 quota::StorageObserver::Filter filter(kMonitorStorageType, it->first);
105 it->second.quota_manager->RemoveStorageObserverForFilter(this, filter); 136 it->second.quota_manager->RemoveStorageObserverForFilter(this, filter);
106 origin_state_map_.erase(it++); 137 origin_state_map_.erase(it++);
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 return ExtensionStorageMonitorFactory::GetForBrowserContext(context); 205 return ExtensionStorageMonitorFactory::GetForBrowserContext(context);
175 } 206 }
176 207
177 ExtensionStorageMonitor::ExtensionStorageMonitor( 208 ExtensionStorageMonitor::ExtensionStorageMonitor(
178 content::BrowserContext* context) 209 content::BrowserContext* context)
179 : enable_for_all_extensions_(false), 210 : enable_for_all_extensions_(false),
180 initial_extension_threshold_(kExtensionInitialThreshold), 211 initial_extension_threshold_(kExtensionInitialThreshold),
181 initial_ephemeral_threshold_(kEphemeralAppInitialThreshold), 212 initial_ephemeral_threshold_(kEphemeralAppInitialThreshold),
182 observer_rate_(kStorageEventRateSec), 213 observer_rate_(kStorageEventRateSec),
183 context_(context), 214 context_(context),
215 extension_prefs_(ExtensionPrefs::Get(context)),
216 extension_registry_observer_(this),
184 weak_ptr_factory_(this) { 217 weak_ptr_factory_(this) {
185 registrar_.Add(this, 218 DCHECK(extension_prefs_);
186 chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, 219
187 content::Source<content::BrowserContext>(context_));
188 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, 220 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
189 content::Source<content::BrowserContext>(context_)); 221 content::Source<content::BrowserContext>(context_));
190 222
191 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); 223 extension_registry_observer_.Add(ExtensionRegistry::Get(context_));
192 DCHECK(registry);
193 registry->AddObserver(this);
194 } 224 }
195 225
196 ExtensionStorageMonitor::~ExtensionStorageMonitor() {} 226 ExtensionStorageMonitor::~ExtensionStorageMonitor() {}
197 227
198 void ExtensionStorageMonitor::Observe( 228 void ExtensionStorageMonitor::Observe(
199 int type, 229 int type,
200 const content::NotificationSource& source, 230 const content::NotificationSource& source,
201 const content::NotificationDetails& details) { 231 const content::NotificationDetails& details) {
202 switch (type) { 232 switch (type) {
203 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: {
204 const Extension* extension =
205 content::Details<const Extension>(details).ptr();
206 RemoveNotificationForExtension(extension->id());
207 break;
208 }
209 case chrome::NOTIFICATION_PROFILE_DESTROYED: { 233 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
210 StopMonitoringAll(); 234 StopMonitoringAll();
211 break; 235 break;
212 } 236 }
213 default: 237 default:
214 NOTREACHED(); 238 NOTREACHED();
215 } 239 }
216 } 240 }
217 241
218 void ExtensionStorageMonitor::OnExtensionLoaded( 242 void ExtensionStorageMonitor::OnExtensionLoaded(
219 content::BrowserContext* browser_context, 243 content::BrowserContext* browser_context,
220 const Extension* extension) { 244 const Extension* extension) {
221 DCHECK(extension);
222 StartMonitoringStorage(extension); 245 StartMonitoringStorage(extension);
223 } 246 }
224 247
225 void ExtensionStorageMonitor::OnExtensionUnloaded( 248 void ExtensionStorageMonitor::OnExtensionUnloaded(
226 content::BrowserContext* browser_context, 249 content::BrowserContext* browser_context,
227 const Extension* extension, 250 const Extension* extension,
228 UnloadedExtensionInfo::Reason reason) { 251 UnloadedExtensionInfo::Reason reason) {
229 DCHECK(extension);
230 StopMonitoringStorage(extension->id()); 252 StopMonitoringStorage(extension->id());
231 } 253 }
232 254
255 void ExtensionStorageMonitor::OnExtensionWillBeInstalled(
256 content::BrowserContext* browser_context,
257 const Extension* extension,
258 bool is_update,
259 bool from_ephemeral,
260 const std::string& old_name) {
261 // If an ephemeral app was promoted to a regular installed app, we may need to
262 // increase its next threshold.
263 if (!from_ephemeral || !ShouldMonitorStorageFor(extension))
264 return;
265
266 if (!enable_for_all_extensions_) {
267 // If monitoring is not enabled for installed extensions, just stop
268 // monitoring.
269 SetNextStorageThreshold(extension->id(), 0);
270 StopMonitoringStorage(extension->id());
271 return;
272 }
273
274 int64 next_threshold = GetNextStorageThresholdFromPrefs(extension->id());
275 if (next_threshold <= initial_extension_threshold_) {
276 // Clear the next threshold in the prefs. This effectively raises it to
277 // |initial_extension_threshold_|. If the current threshold is already
278 // higher than this, leave it as is.
279 SetNextStorageThreshold(extension->id(), 0);
280
281 if (storage_observer_.get()) {
282 BrowserThread::PostTask(
283 BrowserThread::IO,
284 FROM_HERE,
285 base::Bind(&StorageEventObserver::UpdateThresholdForExtension,
286 storage_observer_,
287 extension->id(),
288 initial_extension_threshold_));
289 }
290 }
291 }
292
293 void ExtensionStorageMonitor::OnExtensionUninstalled(
294 content::BrowserContext* browser_context,
295 const Extension* extension) {
296 RemoveNotificationForExtension(extension->id());
297 }
298
233 std::string ExtensionStorageMonitor::GetNotificationId( 299 std::string ExtensionStorageMonitor::GetNotificationId(
234 const std::string& extension_id) { 300 const std::string& extension_id) {
235 std::vector<std::string> placeholders; 301 std::vector<std::string> placeholders;
236 placeholders.push_back(context_->GetPath().BaseName().MaybeAsASCII()); 302 placeholders.push_back(context_->GetPath().BaseName().MaybeAsASCII());
237 placeholders.push_back(extension_id); 303 placeholders.push_back(extension_id);
238 304
239 return ReplaceStringPlaceholders(kNotificationIdFormat, placeholders, NULL); 305 return ReplaceStringPlaceholders(kNotificationIdFormat, placeholders, NULL);
240 } 306 }
241 307
242 void ExtensionStorageMonitor::OnStorageThresholdExceeded( 308 void ExtensionStorageMonitor::OnStorageThresholdExceeded(
243 const std::string& extension_id, 309 const std::string& extension_id,
244 int64 next_threshold, 310 int64 next_threshold,
245 int64 current_usage) { 311 int64 current_usage) {
246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247 313
248 const Extension* extension = ExtensionRegistry::Get(context_)-> 314 const Extension* extension = ExtensionRegistry::Get(context_)->
249 GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); 315 GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
250 if (!extension) 316 if (!extension)
251 return; 317 return;
252 318
253 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_); 319 if (GetNextStorageThreshold(extension->id()) < next_threshold)
254 DCHECK(prefs); 320 SetNextStorageThreshold(extension->id(), next_threshold);
255 prefs->SetNextStorageThreshold(extension->id(), next_threshold);
256 321
257 const int kIconSize = message_center::kNotificationIconSize; 322 const int kIconSize = message_center::kNotificationIconSize;
258 ExtensionResource resource = IconsInfo::GetIconResource( 323 ExtensionResource resource = IconsInfo::GetIconResource(
259 extension, kIconSize, ExtensionIconSet::MATCH_BIGGER); 324 extension, kIconSize, ExtensionIconSet::MATCH_BIGGER);
260 ImageLoader::Get(context_)->LoadImageAsync( 325 ImageLoader::Get(context_)->LoadImageAsync(
261 extension, resource, gfx::Size(kIconSize, kIconSize), 326 extension, resource, gfx::Size(kIconSize, kIconSize),
262 base::Bind(&ExtensionStorageMonitor::OnImageLoaded, 327 base::Bind(&ExtensionStorageMonitor::OnImageLoaded,
263 weak_ptr_factory_.GetWeakPtr(), 328 weak_ptr_factory_.GetWeakPtr(),
264 extension_id, 329 extension_id,
265 current_usage)); 330 current_usage));
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 } 390 }
326 default: 391 default:
327 NOTREACHED(); 392 NOTREACHED();
328 } 393 }
329 } 394 }
330 395
331 void ExtensionStorageMonitor::DisableStorageMonitoring( 396 void ExtensionStorageMonitor::DisableStorageMonitoring(
332 const std::string& extension_id) { 397 const std::string& extension_id) {
333 StopMonitoringStorage(extension_id); 398 StopMonitoringStorage(extension_id);
334 399
335 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_); 400 SetStorageNotificationEnabled(extension_id, false);
336 DCHECK(prefs);
337 prefs->SetStorageNotificationEnabled(extension_id, false);
338 401
339 message_center::MessageCenter::Get()->RemoveNotification( 402 message_center::MessageCenter::Get()->RemoveNotification(
340 GetNotificationId(extension_id), false); 403 GetNotificationId(extension_id), false);
341 } 404 }
342 405
343 void ExtensionStorageMonitor::StartMonitoringStorage( 406 void ExtensionStorageMonitor::StartMonitoringStorage(
344 const Extension* extension) { 407 const Extension* extension) {
345 if (!extension->HasAPIPermission(APIPermission::kUnlimitedStorage)) 408 if (!ShouldMonitorStorageFor(extension))
346 return; 409 return;
347 410
348 // Do not monitor storage for component extensions.
349 if (extension->location() == Manifest::COMPONENT)
350 return;
351
352 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
353 DCHECK(prefs);
354
355 // First apply this feature only to experimental ephemeral apps. If it works 411 // First apply this feature only to experimental ephemeral apps. If it works
356 // well, roll it out to all extensions and apps. 412 // well, roll it out to all extensions and apps.
357 bool is_ephemeral = prefs->IsEphemeralApp(extension->id()); 413 if (!enable_for_all_extensions_ &&
358 if (!is_ephemeral && !enable_for_all_extensions_) 414 !extension_prefs_->IsEphemeralApp(extension->id())) {
359 return; 415 return;
416 }
360 417
361 if (!prefs->IsStorageNotificationEnabled(extension->id())) 418 if (!IsStorageNotificationEnabled(extension->id()))
362 return; 419 return;
363 420
364 // Lazily create the storage monitor proxy on the IO thread. 421 // Lazily create the storage monitor proxy on the IO thread.
365 if (!storage_observer_.get()) { 422 if (!storage_observer_.get()) {
366 storage_observer_ = 423 storage_observer_ =
367 new StorageEventObserver(weak_ptr_factory_.GetWeakPtr()); 424 new StorageEventObserver(weak_ptr_factory_.GetWeakPtr());
368 } 425 }
369 426
370 GURL site_url = 427 GURL site_url =
371 extensions::util::GetSiteForExtensionId(extension->id(), context_); 428 extensions::util::GetSiteForExtensionId(extension->id(), context_);
372 content::StoragePartition* storage_partition = 429 content::StoragePartition* storage_partition =
373 content::BrowserContext::GetStoragePartitionForSite(context_, site_url); 430 content::BrowserContext::GetStoragePartitionForSite(context_, site_url);
374 DCHECK(storage_partition); 431 DCHECK(storage_partition);
375 scoped_refptr<quota::QuotaManager> quota_manager( 432 scoped_refptr<quota::QuotaManager> quota_manager(
376 storage_partition->GetQuotaManager()); 433 storage_partition->GetQuotaManager());
377 434
378 GURL storage_origin(site_url.GetOrigin()); 435 GURL storage_origin(site_url.GetOrigin());
379 if (extension->is_hosted_app()) 436 if (extension->is_hosted_app())
380 storage_origin = AppLaunchInfo::GetLaunchWebURL(extension).GetOrigin(); 437 storage_origin = AppLaunchInfo::GetLaunchWebURL(extension).GetOrigin();
381 438
382 int next_threshold = prefs->GetNextStorageThreshold(extension->id());
383 if (next_threshold == 0) {
384 // The next threshold is written to the prefs after the initial threshold is
385 // exceeded.
386 next_threshold = is_ephemeral ? initial_ephemeral_threshold_
387 : initial_extension_threshold_;
388 }
389
390 BrowserThread::PostTask( 439 BrowserThread::PostTask(
391 BrowserThread::IO, 440 BrowserThread::IO,
392 FROM_HERE, 441 FROM_HERE,
393 base::Bind(&StorageEventObserver::StartObservingForExtension, 442 base::Bind(&StorageEventObserver::StartObservingForExtension,
394 storage_observer_, 443 storage_observer_,
395 quota_manager, 444 quota_manager,
396 extension->id(), 445 extension->id(),
397 storage_origin, 446 storage_origin,
398 next_threshold, 447 GetNextStorageThreshold(extension->id()),
399 observer_rate_)); 448 observer_rate_));
400 } 449 }
401 450
402 void ExtensionStorageMonitor::StopMonitoringStorage( 451 void ExtensionStorageMonitor::StopMonitoringStorage(
403 const std::string& extension_id) { 452 const std::string& extension_id) {
404 if (!storage_observer_.get()) 453 if (!storage_observer_.get())
405 return; 454 return;
406 455
407 BrowserThread::PostTask( 456 BrowserThread::PostTask(
408 BrowserThread::IO, 457 BrowserThread::IO,
409 FROM_HERE, 458 FROM_HERE,
410 base::Bind(&StorageEventObserver::StopObservingForExtension, 459 base::Bind(&StorageEventObserver::StopObservingForExtension,
411 storage_observer_, 460 storage_observer_,
412 extension_id)); 461 extension_id));
413 } 462 }
414 463
415 void ExtensionStorageMonitor::StopMonitoringAll() { 464 void ExtensionStorageMonitor::StopMonitoringAll() {
416 ExtensionRegistry* registry = ExtensionRegistry::Get(context_); 465 extension_registry_observer_.RemoveAll();
417 DCHECK(registry);
418 registry->RemoveObserver(this);
419 466
420 RemoveAllNotifications(); 467 RemoveAllNotifications();
421 468
422 if (!storage_observer_.get()) 469 if (!storage_observer_.get())
423 return; 470 return;
424 471
425 BrowserThread::PostTask( 472 BrowserThread::PostTask(
426 BrowserThread::IO, 473 BrowserThread::IO,
427 FROM_HERE, 474 FROM_HERE,
428 base::Bind(&StorageEventObserver::StopObserving, storage_observer_)); 475 base::Bind(&StorageEventObserver::StopObserving, storage_observer_));
(...skipping 18 matching lines...) Expand all
447 494
448 message_center::MessageCenter* center = message_center::MessageCenter::Get(); 495 message_center::MessageCenter* center = message_center::MessageCenter::Get();
449 DCHECK(center); 496 DCHECK(center);
450 for (std::set<std::string>::iterator it = notified_extension_ids_.begin(); 497 for (std::set<std::string>::iterator it = notified_extension_ids_.begin();
451 it != notified_extension_ids_.end(); ++it) { 498 it != notified_extension_ids_.end(); ++it) {
452 center->RemoveNotification(GetNotificationId(*it), false); 499 center->RemoveNotification(GetNotificationId(*it), false);
453 } 500 }
454 notified_extension_ids_.clear(); 501 notified_extension_ids_.clear();
455 } 502 }
456 503
504 int64 ExtensionStorageMonitor::GetNextStorageThreshold(
505 const std::string& extension_id) const {
506 int next_threshold = GetNextStorageThresholdFromPrefs(extension_id);
507 if (next_threshold == 0) {
508 // The next threshold is written to the prefs after the initial threshold is
509 // exceeded.
510 next_threshold = extension_prefs_->IsEphemeralApp(extension_id)
511 ? initial_ephemeral_threshold_
512 : initial_extension_threshold_;
513 }
514 return next_threshold;
515 }
516
517 void ExtensionStorageMonitor::SetNextStorageThreshold(
518 const std::string& extension_id,
519 int64 next_threshold) {
520 extension_prefs_->UpdateExtensionPref(
521 extension_id,
522 kPrefNextStorageThreshold,
523 next_threshold > 0
524 ? new base::StringValue(base::Int64ToString(next_threshold))
525 : NULL);
526 }
527
528 int64 ExtensionStorageMonitor::GetNextStorageThresholdFromPrefs(
529 const std::string& extension_id) const {
530 std::string next_threshold_str;
531 if (extension_prefs_->ReadPrefAsString(
532 extension_id, kPrefNextStorageThreshold, &next_threshold_str)) {
533 int64 next_threshold;
534 if (base::StringToInt64(next_threshold_str, &next_threshold))
535 return next_threshold;
536 }
537
538 // A return value of zero indicates that the initial threshold has not yet
539 // been reached.
540 return 0;
541 }
542
543 bool ExtensionStorageMonitor::IsStorageNotificationEnabled(
544 const std::string& extension_id) const {
545 bool disable_notifications;
546 if (extension_prefs_->ReadPrefAsBoolean(extension_id,
547 kPrefDisableStorageNotifications,
548 &disable_notifications)) {
549 return !disable_notifications;
550 }
551
552 return true;
553 }
554
555 void ExtensionStorageMonitor::SetStorageNotificationEnabled(
556 const std::string& extension_id,
557 bool enable_notifications) {
558 extension_prefs_->UpdateExtensionPref(
559 extension_id,
560 kPrefDisableStorageNotifications,
561 enable_notifications ? NULL : new base::FundamentalValue(true));
562 }
563
457 } // namespace extensions 564 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698