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