Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_storage_monitor.cc
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/extension_storage_monitor.h"
6
7 #include <map>
8
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_storage_monitor_factory.h"
16 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "extensions/browser/extension_prefs.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/browser/image_loader.h"
29 #include "extensions/browser/uninstall_reason.h"
30 #include "extensions/common/extension.h"
31 #include "extensions/common/manifest_handlers/icons_handler.h"
32 #include "extensions/common/permissions/permissions_data.h"
33 #include "storage/browser/quota/quota_manager.h"
34 #include "storage/browser/quota/storage_observer.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/message_center/message_center.h"
37 #include "ui/message_center/notifier_settings.h"
38 #include "ui/message_center/views/constants.h"
39
40 using content::BrowserThread;
41
42 namespace extensions {
43
44 namespace {
45
46 // The rate at which we would like to observe storage events.
47 const int kStorageEventRateSec = 30;
48
49 // Set the thresholds for the first notification. Ephemeral apps have a lower
50 // threshold than installed extensions and apps. Once a threshold is exceeded,
51 // it will be doubled to throttle notifications.
52 const int64 kMBytes = 1024 * 1024;
53 const int64 kEphemeralAppInitialThreshold = 250 * kMBytes;
54 const int64 kExtensionInitialThreshold = 1000 * kMBytes;
55
56 // Notifications have an ID so that we can update them.
57 const char kNotificationIdFormat[] = "ExtensionStorageMonitor-$1-$2";
58 const char kSystemNotifierId[] = "ExtensionStorageMonitor";
59
60 // A preference that stores the next threshold for displaying a notification
61 // when an extension or app consumes excessive disk space. This will not be
62 // set until the extension/app reaches the initial threshold.
63 const char kPrefNextStorageThreshold[] = "next_storage_threshold";
64
65 // If this preference is set to true, notifications will be suppressed when an
66 // extension or app consumes excessive disk space.
67 const char kPrefDisableStorageNotifications[] = "disable_storage_notifications";
68
69 bool ShouldMonitorStorageFor(const Extension* extension) {
70   // Only monitor storage for extensions that are granted unlimited storage.
71   // Do not monitor storage for component extensions.
72   return extension->permissions_data()->HasAPIPermission(
73              APIPermission::kUnlimitedStorage) &&
74          extension->location() != Manifest::COMPONENT;
75 }
76
77 bool ShouldGatherMetricsFor(const Extension* extension) {
78   // We want to know the usage of hosted apps' storage.
79   return ShouldMonitorStorageFor(extension) && extension->is_hosted_app();
80 }
81
82 const Extension* GetExtensionById(content::BrowserContext* context,
83                                   const std::string& extension_id) {
84   return ExtensionRegistry::Get(context)->GetExtensionById(
85       extension_id, ExtensionRegistry::EVERYTHING);
86 }
87
88 void LogTemporaryStorageUsage(int64 usage,
89                               storage::QuotaStatusCode status,
90                               int64 global_quota) {
91   if (status == storage::kQuotaStatusOk) {
92     int64 per_app_quota =
93         global_quota / storage::QuotaManager::kPerHostTemporaryPortion;
94     // Note we use COUNTS_100 (instead of PERCENT) because this can potentially
95     // exceed 100%.
96     UMA_HISTOGRAM_COUNTS_100(
97         "Extensions.HostedAppUnlimitedStorageTemporaryStorageUsage",
98         100.0 * usage / per_app_quota);
99   }
100 }
101
102 }  // namespace
103
104 // StorageEventObserver monitors the storage usage of extensions and lives on
105 // the IO thread. When a threshold is exceeded, a message will be posted to the
106 // UI thread, which displays the notification.
107 class StorageEventObserver
108     : public base::RefCountedThreadSafe<StorageEventObserver,
109                                         BrowserThread::DeleteOnIOThread>,
110       public storage::StorageObserver {
111  public:
112   explicit StorageEventObserver(
113       base::WeakPtr<ExtensionStorageMonitor> storage_monitor)
114       : storage_monitor_(storage_monitor) {
115   }
116
117   // Register as an observer for the extension's storage events.
118   void StartObservingForExtension(
119       scoped_refptr<storage::QuotaManager> quota_manager,
120       const std::string& extension_id,
121       const GURL& site_url,
122       int64 next_threshold,
123       const base::TimeDelta& rate,
124       bool should_uma) {
125     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
126     DCHECK(quota_manager.get());
127
128     GURL origin = site_url.GetOrigin();
129     StorageState& state = origin_state_map_[origin];
130     state.quota_manager = quota_manager;
131     state.extension_id = extension_id;
132     state.next_threshold = next_threshold;
133     state.should_uma = should_uma;
134
135     // We always observe persistent storage usage.
136     storage::StorageObserver::MonitorParams params(
137         storage::kStorageTypePersistent, origin, rate, false);
138     quota_manager->AddStorageObserver(this, params);
139     if (should_uma) {
140       // And if this is for uma, we also observe temporary storage usage.
141       MonitorParams temporary_params(
142           storage::kStorageTypeTemporary, origin, rate, false);
143       quota_manager->AddStorageObserver(this, temporary_params);
144     }
145   }
146
147   // Updates the threshold for an extension already being monitored.
148   void UpdateThresholdForExtension(const std::string& extension_id,
149                                    int64 next_threshold) {
150     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
151
152     for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
153          it != origin_state_map_.end();
154          ++it) {
155       if (it->second.extension_id == extension_id) {
156         it->second.next_threshold = next_threshold;
157         break;
158       }
159     }
160   }
161
162   // Deregister as an observer for the extension's storage events.
163   void StopObservingForExtension(const std::string& extension_id) {
164     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
165
166     for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
167          it != origin_state_map_.end(); ) {
168       if (it->second.extension_id == extension_id) {
169         storage::StorageObserver::Filter filter(
170             storage::kStorageTypePersistent, it->first);
171         it->second.quota_manager->RemoveStorageObserverForFilter(this, filter);
172         // We also need to unregister temporary storage observation, if this was
173         // being tracked for uma.
174         if (it->second.should_uma) {
175           storage::StorageObserver::Filter temporary_filter(
176               storage::kStorageTypeTemporary, it->first);
177           it->second.quota_manager->RemoveStorageObserverForFilter(this,
178                                                                    filter);
179         }
180         origin_state_map_.erase(it++);
181       } else {
182         ++it;
183       }
184     }
185   }
186
187   // Stop observing all storage events. Called during shutdown.
188   void StopObserving() {
189     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190
191     for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
192          it != origin_state_map_.end(); ++it) {
193       it->second.quota_manager->RemoveStorageObserver(this);
194     }
195     origin_state_map_.clear();
196   }
197
198  private:
199   friend class base::DeleteHelper<StorageEventObserver>;
200   friend struct content::BrowserThread::DeleteOnThread<
201       content::BrowserThread::IO>;
202
203   struct StorageState {
204     scoped_refptr<storage::QuotaManager> quota_manager;
205
206     std::string extension_id;
207
208     // If |next_threshold| is -1, it signifies that we should not enforce (and
209     // only track) storage for this extension.
210     int64 next_threshold;
211
212     bool should_uma;
213
214     StorageState() : next_threshold(-1), should_uma(false) {}
215   };
216   typedef std::map<GURL, StorageState> OriginStorageStateMap;
217
218   ~StorageEventObserver() override {
219     DCHECK(origin_state_map_.empty());
220     StopObserving();
221   }
222
223   // storage::StorageObserver implementation.
224   void OnStorageEvent(const Event& event) override {
225     OriginStorageStateMap::iterator iter =
226         origin_state_map_.find(event.filter.origin);
227     if (iter == origin_state_map_.end())
228       return;
229     StorageState& state = iter->second;
230
231     if (state.should_uma) {
232       if (event.filter.storage_type == storage::kStorageTypePersistent) {
233         UMA_HISTOGRAM_MEMORY_KB(
234             "Extensions.HostedAppUnlimitedStoragePersistentStorageUsage",
235             event.usage);
236       } else {
237         // We can't use the quota in the event because it assumes unlimited
238         // storage.
239         BrowserThread::PostTask(
240             BrowserThread::IO,
241             FROM_HERE,
242             base::Bind(&storage::QuotaManager::GetTemporaryGlobalQuota,
243                        state.quota_manager,
244                        base::Bind(&LogTemporaryStorageUsage, event.usage)));
245       }
246     }
247
248     if (state.next_threshold != -1 &&
249         event.usage >= state.next_threshold) {
250       while (event.usage >= state.next_threshold)
251         state.next_threshold *= 2;
252
253       BrowserThread::PostTask(
254           BrowserThread::UI,
255           FROM_HERE,
256           base::Bind(&ExtensionStorageMonitor::OnStorageThresholdExceeded,
257                      storage_monitor_,
258                      state.extension_id,
259                      state.next_threshold,
260                      event.usage));
261     }
262   }
263
264   OriginStorageStateMap origin_state_map_;
265   base::WeakPtr<ExtensionStorageMonitor> storage_monitor_;
266 };
267
268 // ExtensionStorageMonitor
269
270 // static
271 ExtensionStorageMonitor* ExtensionStorageMonitor::Get(
272     content::BrowserContext* context) {
273   return ExtensionStorageMonitorFactory::GetForBrowserContext(context);
274 }
275
276 ExtensionStorageMonitor::ExtensionStorageMonitor(
277     content::BrowserContext* context)
278     : enable_for_all_extensions_(false),
279       initial_extension_threshold_(kExtensionInitialThreshold),
280       initial_ephemeral_threshold_(kEphemeralAppInitialThreshold),
281       observer_rate_(base::TimeDelta::FromSeconds(kStorageEventRateSec)),
282       context_(context),
283       extension_prefs_(ExtensionPrefs::Get(context)),
284       extension_registry_observer_(this),
285       weak_ptr_factory_(this) {
286   DCHECK(extension_prefs_);
287
288   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
289                  content::Source<content::BrowserContext>(context_));
290
291   extension_registry_observer_.Add(ExtensionRegistry::Get(context_));
292 }
293
294 ExtensionStorageMonitor::~ExtensionStorageMonitor() {}
295
296 void ExtensionStorageMonitor::Observe(
297     int type,
298     const content::NotificationSource& source,
299     const content::NotificationDetails& details) {
300   switch (type) {
301     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
302       StopMonitoringAll();
303       break;
304     }
305     default:
306       NOTREACHED();
307   }
308 }
309
310 void ExtensionStorageMonitor::OnExtensionLoaded(
311     content::BrowserContext* browser_context,
312     const Extension* extension) {
313   StartMonitoringStorage(extension);
314 }
315
316 void ExtensionStorageMonitor::OnExtensionUnloaded(
317     content::BrowserContext* browser_context,
318     const Extension* extension,
319     UnloadedExtensionInfo::Reason reason) {
320   StopMonitoringStorage(extension->id());
321 }
322
323 void ExtensionStorageMonitor::OnExtensionWillBeInstalled(
324     content::BrowserContext* browser_context,
325     const Extension* extension,
326     bool is_update,
327     bool from_ephemeral,
328     const std::string& old_name) {
329   // If an ephemeral app was promoted to a regular installed app, we may need to
330   // increase its next threshold.
331   if (!from_ephemeral || !ShouldMonitorStorageFor(extension))
332     return;
333
334   if (!enable_for_all_extensions_) {
335     // If monitoring is not enabled for installed extensions, just stop
336     // monitoring.
337     SetNextStorageThreshold(extension->id(), 0);
338     StopMonitoringStorage(extension->id());
339     return;
340   }
341
342   int64 next_threshold = GetNextStorageThresholdFromPrefs(extension->id());
343   if (next_threshold <= initial_extension_threshold_) {
344     // Clear the next threshold in the prefs. This effectively raises it to
345     // |initial_extension_threshold_|. If the current threshold is already
346     // higher than this, leave it as is.
347     SetNextStorageThreshold(extension->id(), 0);
348
349     if (storage_observer_.get()) {
350       BrowserThread::PostTask(
351           BrowserThread::IO,
352           FROM_HERE,
353           base::Bind(&StorageEventObserver::UpdateThresholdForExtension,
354                      storage_observer_,
355                      extension->id(),
356                      initial_extension_threshold_));
357     }
358   }
359 }
360
361 void ExtensionStorageMonitor::OnExtensionUninstalled(
362     content::BrowserContext* browser_context,
363     const Extension* extension,
364     extensions::UninstallReason reason) {
365   RemoveNotificationForExtension(extension->id());
366 }
367
368 void ExtensionStorageMonitor::ExtensionUninstallAccepted() {
369   DCHECK(!uninstall_extension_id_.empty());
370
371   const Extension* extension = GetExtensionById(context_,
372                                                 uninstall_extension_id_);
373   uninstall_extension_id_.clear();
374   if (!extension)
375     return;
376
377   ExtensionService* service =
378       ExtensionSystem::Get(context_)->extension_service();
379   DCHECK(service);
380   service->UninstallExtension(
381       extension->id(),
382       extensions::UNINSTALL_REASON_STORAGE_THRESHOLD_EXCEEDED,
383       base::Bind(&base::DoNothing),
384       NULL);
385 }
386
387 void ExtensionStorageMonitor::ExtensionUninstallCanceled() {
388   uninstall_extension_id_.clear();
389 }
390
391 std::string ExtensionStorageMonitor::GetNotificationId(
392     const std::string& extension_id) {
393   std::vector<std::string> placeholders;
394   placeholders.push_back(context_->GetPath().BaseName().MaybeAsASCII());
395   placeholders.push_back(extension_id);
396
397   return ReplaceStringPlaceholders(kNotificationIdFormat, placeholders, NULL);
398 }
399
400 void ExtensionStorageMonitor::OnStorageThresholdExceeded(
401     const std::string& extension_id,
402     int64 next_threshold,
403     int64 current_usage) {
404   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
405
406   const Extension* extension = GetExtensionById(context_, extension_id);
407   if (!extension)
408     return;
409
410   if (GetNextStorageThreshold(extension->id()) < next_threshold)
411     SetNextStorageThreshold(extension->id(), next_threshold);
412
413   const int kIconSize = message_center::kNotificationIconSize;
414   ExtensionResource resource =  IconsInfo::GetIconResource(
415       extension, kIconSize, ExtensionIconSet::MATCH_BIGGER);
416   ImageLoader::Get(context_)->LoadImageAsync(
417       extension, resource, gfx::Size(kIconSize, kIconSize),
418       base::Bind(&ExtensionStorageMonitor::OnImageLoaded,
419                  weak_ptr_factory_.GetWeakPtr(),
420                  extension_id,
421                  current_usage));
422 }
423
424 void ExtensionStorageMonitor::OnImageLoaded(
425     const std::string& extension_id,
426     int64 current_usage,
427     const gfx::Image& image) {
428   const Extension* extension = GetExtensionById(context_, extension_id);
429   if (!extension)
430     return;
431
432   // Remove any existing notifications to force a new notification to pop up.
433   std::string notification_id(GetNotificationId(extension_id));
434   message_center::MessageCenter::Get()->RemoveNotification(
435       notification_id, false);
436
437   message_center::RichNotificationData notification_data;
438   notification_data.buttons.push_back(message_center::ButtonInfo(
439       l10n_util::GetStringUTF16(extension->is_app() ?
440           IDS_EXTENSION_STORAGE_MONITOR_BUTTON_DISMISS_APP :
441           IDS_EXTENSION_STORAGE_MONITOR_BUTTON_DISMISS_EXTENSION)));
442   notification_data.buttons.push_back(message_center::ButtonInfo(
443       l10n_util::GetStringUTF16(extension->is_app() ?
444           IDS_EXTENSION_STORAGE_MONITOR_BUTTON_UNINSTALL_APP :
445           IDS_EXTENSION_STORAGE_MONITOR_BUTTON_UNINSTALL_EXTENSION)));
446
447   gfx::Image notification_image(image);
448   if (notification_image.IsEmpty()) {
449     notification_image =
450         extension->is_app() ? gfx::Image(util::GetDefaultAppIcon())
451                             : gfx::Image(util::GetDefaultExtensionIcon());
452   }
453
454   scoped_ptr<message_center::Notification> notification;
455   notification.reset(new message_center::Notification(
456       message_center::NOTIFICATION_TYPE_SIMPLE,
457       notification_id,
458       l10n_util::GetStringUTF16(IDS_EXTENSION_STORAGE_MONITOR_TITLE),
459       l10n_util::GetStringFUTF16(
460           IDS_EXTENSION_STORAGE_MONITOR_TEXT,
461           base::UTF8ToUTF16(extension->name()),
462           base::IntToString16(current_usage / kMBytes)),
463       notification_image,
464       base::string16() /* display source */,
465       message_center::NotifierId(
466           message_center::NotifierId::SYSTEM_COMPONENT, kSystemNotifierId),
467       notification_data,
468       new message_center::HandleNotificationButtonClickDelegate(base::Bind(
469           &ExtensionStorageMonitor::OnNotificationButtonClick,
470           weak_ptr_factory_.GetWeakPtr(),
471           extension_id))));
472   notification->SetSystemPriority();
473   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
474
475   notified_extension_ids_.insert(extension_id);
476 }
477
478 void ExtensionStorageMonitor::OnNotificationButtonClick(
479     const std::string& extension_id, int button_index) {
480   switch (button_index) {
481     case BUTTON_DISABLE_NOTIFICATION: {
482       DisableStorageMonitoring(extension_id);
483       break;
484     }
485     case BUTTON_UNINSTALL: {
486       ShowUninstallPrompt(extension_id);
487       break;
488     }
489     default:
490       NOTREACHED();
491   }
492 }
493
494 void ExtensionStorageMonitor::DisableStorageMonitoring(
495     const std::string& extension_id) {
496   scoped_refptr<const Extension> extension =
497       ExtensionRegistry::Get(context_)->enabled_extensions().GetByID(
498           extension_id);
499   if (!extension.get() || !ShouldGatherMetricsFor(extension.get()))
500     StopMonitoringStorage(extension_id);
501
502   SetStorageNotificationEnabled(extension_id, false);
503
504   message_center::MessageCenter::Get()->RemoveNotification(
505       GetNotificationId(extension_id), false);
506 }
507
508 void ExtensionStorageMonitor::StartMonitoringStorage(
509     const Extension* extension) {
510   if (!ShouldMonitorStorageFor(extension))
511     return;
512
513   // First apply this feature only to experimental ephemeral apps. If it works
514   // well, roll it out to all extensions and apps.
515   bool should_enforce =
516       (enable_for_all_extensions_ ||
517        extension_prefs_->IsEphemeralApp(extension->id())) &&
518       IsStorageNotificationEnabled(extension->id());
519
520   bool for_metrics = ShouldGatherMetricsFor(extension);
521
522   if (!should_enforce && !for_metrics)
523     return;  // Don't track this extension.
524
525   // Lazily create the storage monitor proxy on the IO thread.
526   if (!storage_observer_.get()) {
527     storage_observer_ =
528         new StorageEventObserver(weak_ptr_factory_.GetWeakPtr());
529   }
530
531   GURL site_url = util::GetSiteForExtensionId(extension->id(), context_);
532   content::StoragePartition* storage_partition =
533       content::BrowserContext::GetStoragePartitionForSite(context_, site_url);
534   DCHECK(storage_partition);
535   scoped_refptr<storage::QuotaManager> quota_manager(
536       storage_partition->GetQuotaManager());
537
538   GURL storage_origin(site_url.GetOrigin());
539   if (extension->is_hosted_app())
540     storage_origin = AppLaunchInfo::GetLaunchWebURL(extension).GetOrigin();
541
542   // Don't give a threshold if we're not enforcing.
543   int next_threshold =
544       should_enforce ? GetNextStorageThreshold(extension->id()) : -1;
545
546   BrowserThread::PostTask(
547       BrowserThread::IO,
548       FROM_HERE,
549       base::Bind(&StorageEventObserver::StartObservingForExtension,
550                  storage_observer_,
551                  quota_manager,
552                  extension->id(),
553                  storage_origin,
554                  next_threshold,
555                  observer_rate_,
556                  for_metrics));
557 }
558
559 void ExtensionStorageMonitor::StopMonitoringStorage(
560     const std::string& extension_id) {
561   if (!storage_observer_.get())
562     return;
563
564   BrowserThread::PostTask(
565       BrowserThread::IO,
566       FROM_HERE,
567       base::Bind(&StorageEventObserver::StopObservingForExtension,
568                  storage_observer_,
569                  extension_id));
570 }
571
572 void ExtensionStorageMonitor::StopMonitoringAll() {
573   extension_registry_observer_.RemoveAll();
574
575   RemoveAllNotifications();
576
577   if (!storage_observer_.get())
578     return;
579
580   BrowserThread::PostTask(
581       BrowserThread::IO,
582       FROM_HERE,
583       base::Bind(&StorageEventObserver::StopObserving, storage_observer_));
584   storage_observer_ = NULL;
585 }
586
587 void ExtensionStorageMonitor::RemoveNotificationForExtension(
588     const std::string& extension_id) {
589   std::set<std::string>::iterator ext_id =
590       notified_extension_ids_.find(extension_id);
591   if (ext_id == notified_extension_ids_.end())
592     return;
593
594   notified_extension_ids_.erase(ext_id);
595   message_center::MessageCenter::Get()->RemoveNotification(
596       GetNotificationId(extension_id), false);
597 }
598
599 void ExtensionStorageMonitor::RemoveAllNotifications() {
600   if (notified_extension_ids_.empty())
601     return;
602
603   message_center::MessageCenter* center = message_center::MessageCenter::Get();
604   DCHECK(center);
605   for (std::set<std::string>::iterator it = notified_extension_ids_.begin();
606        it != notified_extension_ids_.end(); ++it) {
607     center->RemoveNotification(GetNotificationId(*it), false);
608   }
609   notified_extension_ids_.clear();
610 }
611
612 void ExtensionStorageMonitor::ShowUninstallPrompt(
613     const std::string& extension_id) {
614   const Extension* extension = GetExtensionById(context_, extension_id);
615   if (!extension)
616     return;
617
618   if (!uninstall_dialog_.get()) {
619     uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
620         Profile::FromBrowserContext(context_), NULL, this));
621   }
622
623   uninstall_extension_id_ = extension->id();
624   uninstall_dialog_->ConfirmUninstall(extension);
625 }
626
627 int64 ExtensionStorageMonitor::GetNextStorageThreshold(
628     const std::string& extension_id) const {
629   int next_threshold = GetNextStorageThresholdFromPrefs(extension_id);
630   if (next_threshold == 0) {
631     // The next threshold is written to the prefs after the initial threshold is
632     // exceeded.
633     next_threshold = extension_prefs_->IsEphemeralApp(extension_id)
634                          ? initial_ephemeral_threshold_
635                          : initial_extension_threshold_;
636   }
637   return next_threshold;
638 }
639
640 void ExtensionStorageMonitor::SetNextStorageThreshold(
641     const std::string& extension_id,
642     int64 next_threshold) {
643   extension_prefs_->UpdateExtensionPref(
644       extension_id,
645       kPrefNextStorageThreshold,
646       next_threshold > 0
647           ? new base::StringValue(base::Int64ToString(next_threshold))
648           : NULL);
649 }
650
651 int64 ExtensionStorageMonitor::GetNextStorageThresholdFromPrefs(
652     const std::string& extension_id) const {
653   std::string next_threshold_str;
654   if (extension_prefs_->ReadPrefAsString(
655           extension_id, kPrefNextStorageThreshold, &next_threshold_str)) {
656     int64 next_threshold;
657     if (base::StringToInt64(next_threshold_str, &next_threshold))
658       return next_threshold;
659   }
660
661   // A return value of zero indicates that the initial threshold has not yet
662   // been reached.
663   return 0;
664 }
665
666 bool ExtensionStorageMonitor::IsStorageNotificationEnabled(
667     const std::string& extension_id) const {
668   bool disable_notifications;
669   if (extension_prefs_->ReadPrefAsBoolean(extension_id,
670                                           kPrefDisableStorageNotifications,
671                                           &disable_notifications)) {
672     return !disable_notifications;
673   }
674
675   return true;
676 }
677
678 void ExtensionStorageMonitor::SetStorageNotificationEnabled(
679     const std::string& extension_id,
680     bool enable_notifications) {
681   extension_prefs_->UpdateExtensionPref(
682       extension_id,
683       kPrefDisableStorageNotifications,
684       enable_notifications ? NULL : new base::FundamentalValue(true));
685 }
686
687 }  // namespace extensions