| Index: chrome/browser/extensions/extension_storage_monitor.cc | 
| diff --git a/chrome/browser/extensions/extension_storage_monitor.cc b/chrome/browser/extensions/extension_storage_monitor.cc | 
| index 530adf8cb79572c9f0a31fd79429cf9806ce5c95..217df817d34e606a7edf49f8c584d6e4c5e7c58a 100644 | 
| --- a/chrome/browser/extensions/extension_storage_monitor.cc | 
| +++ b/chrome/browser/extensions/extension_storage_monitor.cc | 
| @@ -45,7 +45,7 @@ namespace extensions { | 
| namespace { | 
|  | 
| // The rate at which we would like to observe storage events. | 
| -const int kStorageEventRateSec = 30; | 
| +constexpr base::TimeDelta kStorageEventRate = base::TimeDelta::FromSeconds(30); | 
|  | 
| // Set the thresholds for the first notification. Once a threshold is exceeded, | 
| // it will be doubled to throttle notifications. | 
| @@ -99,20 +99,81 @@ void LogTemporaryStorageUsage( | 
|  | 
| }  // namespace | 
|  | 
| -// StorageEventObserver monitors the storage usage of extensions and lives on | 
| -// the IO thread. When a threshold is exceeded, a message will be posted to the | 
| -// UI thread, which displays the notification. | 
| -class StorageEventObserver | 
| -    : public base::RefCountedThreadSafe<StorageEventObserver, | 
| -                                        BrowserThread::DeleteOnIOThread>, | 
| -      public storage::StorageObserver { | 
| +// SingleExtensionStorageObserver monitors the storage usage of one extension, | 
| +// and lives on the IO thread. When a threshold is exceeded, a message will be | 
| +// posted to the ExtensionStorageMonitor on the UI thread, which displays the | 
| +// notification. | 
| +class SingleExtensionStorageObserver : public storage::StorageObserver { | 
| public: | 
| -  explicit StorageEventObserver( | 
| -      base::WeakPtr<ExtensionStorageMonitor> storage_monitor) | 
| -      : storage_monitor_(storage_monitor) { | 
| +  SingleExtensionStorageObserver( | 
| +      ExtensionStorageMonitorIOHelper* io_helper, | 
| +      const std::string& extension_id, | 
| +      scoped_refptr<storage::QuotaManager> quota_manager, | 
| +      const GURL& origin, | 
| +      int64_t next_threshold, | 
| +      base::TimeDelta rate, | 
| +      bool should_uma) | 
| +      : io_helper_(io_helper), | 
| +        extension_id_(extension_id), | 
| +        quota_manager_(std::move(quota_manager)), | 
| +        next_threshold_(next_threshold), | 
| +        should_uma_(should_uma) { | 
| +    // We always observe persistent storage usage. | 
| +    storage::StorageObserver::MonitorParams params( | 
| +        storage::kStorageTypePersistent, origin, rate, false); | 
| +    quota_manager_->AddStorageObserver(this, params); | 
| +    if (should_uma) { | 
| +      // And if this is for uma, we also observe temporary storage usage. | 
| +      MonitorParams temporary_params(storage::kStorageTypeTemporary, origin, | 
| +                                     rate, false); | 
| +      quota_manager_->AddStorageObserver(this, temporary_params); | 
| +    } | 
| +  } | 
| + | 
| +  ~SingleExtensionStorageObserver() override { | 
| +    // This removes all our registrations. | 
| +    quota_manager_->RemoveStorageObserver(this); | 
| +  } | 
| + | 
| +  void set_next_threshold(int64_t next_threshold) { | 
| +    next_threshold_ = next_threshold; | 
| } | 
|  | 
| -  // Register as an observer for the extension's storage events. | 
| +  // storage::StorageObserver implementation. | 
| +  void OnStorageEvent(const Event& event) override; | 
| + | 
| + private: | 
| +  // The IO thread helper that owns this instance. | 
| +  ExtensionStorageMonitorIOHelper* const io_helper_; | 
| + | 
| +  // The extension associated with the origin under observation. | 
| +  const std::string extension_id_; | 
| + | 
| +  // The quota manager being observed, corresponding to the extension's storage | 
| +  // partition. | 
| +  scoped_refptr<storage::QuotaManager> quota_manager_; | 
| + | 
| +  // If |next_threshold| is -1, it signifies that we should not enforce (and | 
| +  // only track) storage for this extension. | 
| +  int64_t next_threshold_; | 
| + | 
| +  const bool should_uma_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(SingleExtensionStorageObserver); | 
| +}; | 
| + | 
| +// The IO thread part of ExtensionStorageMonitor. This class manages a flock of | 
| +// SingleExtensionStorageObserver instances, one for each tracked extension. | 
| +// This class is owned by, and reports back to, ExtensionStorageMonitor. | 
| +class ExtensionStorageMonitorIOHelper | 
| +    : public base::RefCountedThreadSafe<ExtensionStorageMonitorIOHelper, | 
| +                                        BrowserThread::DeleteOnIOThread> { | 
| + public: | 
| +  explicit ExtensionStorageMonitorIOHelper( | 
| +      base::WeakPtr<ExtensionStorageMonitor> extension_storage_monitor) | 
| +      : extension_storage_monitor_(std::move(extension_storage_monitor)) {} | 
| + | 
| +  // Register a StorageObserver for the extension's storage events. | 
| void StartObservingForExtension( | 
| scoped_refptr<storage::QuotaManager> quota_manager, | 
| const std::string& extension_id, | 
| @@ -123,23 +184,12 @@ class StorageEventObserver | 
| DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
| DCHECK(quota_manager.get()); | 
|  | 
| -    GURL origin = site_url.GetOrigin(); | 
| -    StorageState& state = origin_state_map_[origin]; | 
| -    state.quota_manager = quota_manager; | 
| -    state.extension_id = extension_id; | 
| -    state.next_threshold = next_threshold; | 
| -    state.should_uma = should_uma; | 
| +    DCHECK(!FindObserver(extension_id)); | 
|  | 
| -    // We always observe persistent storage usage. | 
| -    storage::StorageObserver::MonitorParams params( | 
| -        storage::kStorageTypePersistent, origin, rate, false); | 
| -    quota_manager->AddStorageObserver(this, params); | 
| -    if (should_uma) { | 
| -      // And if this is for uma, we also observe temporary storage usage. | 
| -      MonitorParams temporary_params( | 
| -          storage::kStorageTypeTemporary, origin, rate, false); | 
| -      quota_manager->AddStorageObserver(this, temporary_params); | 
| -    } | 
| +    storage_observers_[extension_id] = | 
| +        base::MakeUnique<SingleExtensionStorageObserver>( | 
| +            this, extension_id, std::move(quota_manager), site_url.GetOrigin(), | 
| +            next_threshold, rate, should_uma); | 
| } | 
|  | 
| // Updates the threshold for an extension already being monitored. | 
| @@ -147,116 +197,76 @@ class StorageEventObserver | 
| int64_t next_threshold) { | 
| DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | 
| -    for (OriginStorageStateMap::iterator it = origin_state_map_.begin(); | 
| -         it != origin_state_map_.end(); | 
| -         ++it) { | 
| -      if (it->second.extension_id == extension_id) { | 
| -        it->second.next_threshold = next_threshold; | 
| -        break; | 
| -      } | 
| -    } | 
| +    // Note that |extension_id| may not be in the map, since some extensions may | 
| +    // be exempt from monitoring. | 
| +    SingleExtensionStorageObserver* observer = FindObserver(extension_id); | 
| +    if (observer) | 
| +      observer->set_next_threshold(next_threshold); | 
| } | 
|  | 
| // Deregister as an observer for the extension's storage events. | 
| void StopObservingForExtension(const std::string& extension_id) { | 
| DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
|  | 
| -    for (OriginStorageStateMap::iterator it = origin_state_map_.begin(); | 
| -         it != origin_state_map_.end(); ) { | 
| -      if (it->second.extension_id == extension_id) { | 
| -        storage::StorageObserver::Filter filter( | 
| -            storage::kStorageTypePersistent, it->first); | 
| -        it->second.quota_manager->RemoveStorageObserverForFilter(this, filter); | 
| -        // We also need to unregister temporary storage observation, if this was | 
| -        // being tracked for uma. | 
| -        if (it->second.should_uma) { | 
| -          storage::StorageObserver::Filter temporary_filter( | 
| -              storage::kStorageTypeTemporary, it->first); | 
| -          it->second.quota_manager->RemoveStorageObserverForFilter(this, | 
| -                                                                   filter); | 
| -        } | 
| -        origin_state_map_.erase(it++); | 
| -      } else { | 
| -        ++it; | 
| -      } | 
| -    } | 
| +    // Note that |extension_id| may not be in the map, since some extensions may | 
| +    // be exempt from monitoring. | 
| +    storage_observers_.erase(extension_id); | 
| } | 
|  | 
| -  // Stop observing all storage events. Called during shutdown. | 
| -  void StopObserving() { | 
| -    DCHECK_CURRENTLY_ON(BrowserThread::IO); | 
| - | 
| -    for (OriginStorageStateMap::iterator it = origin_state_map_.begin(); | 
| -         it != origin_state_map_.end(); ++it) { | 
| -      it->second.quota_manager->RemoveStorageObserver(this); | 
| -    } | 
| -    origin_state_map_.clear(); | 
| +  base::WeakPtr<ExtensionStorageMonitor> extension_storage_monitor() { | 
| +    return extension_storage_monitor_; | 
| } | 
|  | 
| private: | 
| -  friend class base::DeleteHelper<StorageEventObserver>; | 
| +  friend class base::DeleteHelper<ExtensionStorageMonitorIOHelper>; | 
| friend struct content::BrowserThread::DeleteOnThread< | 
| content::BrowserThread::IO>; | 
|  | 
| -  struct StorageState { | 
| -    scoped_refptr<storage::QuotaManager> quota_manager; | 
| +  ~ExtensionStorageMonitorIOHelper() {} | 
|  | 
| -    std::string extension_id; | 
| - | 
| -    // If |next_threshold| is -1, it signifies that we should not enforce (and | 
| -    // only track) storage for this extension. | 
| -    int64_t next_threshold; | 
| +  SingleExtensionStorageObserver* FindObserver( | 
| +      const std::string& extension_id) { | 
| +    auto it = storage_observers_.find(extension_id); | 
| +    if (it != storage_observers_.end()) | 
| +      return it->second.get(); | 
| +    return nullptr; | 
| +  } | 
|  | 
| -    bool should_uma; | 
| +  // Keys are extension IDs. Values are self-registering StorageObservers. | 
| +  std::map<std::string, std::unique_ptr<SingleExtensionStorageObserver>> | 
| +      storage_observers_; | 
|  | 
| -    StorageState() : next_threshold(-1), should_uma(false) {} | 
| -  }; | 
| -  typedef std::map<GURL, StorageState> OriginStorageStateMap; | 
| +  base::WeakPtr<ExtensionStorageMonitor> extension_storage_monitor_; | 
|  | 
| -  ~StorageEventObserver() override { | 
| -    DCHECK(origin_state_map_.empty()); | 
| -    StopObserving(); | 
| -  } | 
| +  DISALLOW_COPY_AND_ASSIGN(ExtensionStorageMonitorIOHelper); | 
| +}; | 
|  | 
| -  // storage::StorageObserver implementation. | 
| -  void OnStorageEvent(const Event& event) override { | 
| -    OriginStorageStateMap::iterator iter = | 
| -        origin_state_map_.find(event.filter.origin); | 
| -    if (iter == origin_state_map_.end()) | 
| -      return; | 
| -    StorageState& state = iter->second; | 
| - | 
| -    if (state.should_uma) { | 
| -      if (event.filter.storage_type == storage::kStorageTypePersistent) { | 
| -        UMA_HISTOGRAM_MEMORY_KB( | 
| -            "Extensions.HostedAppUnlimitedStoragePersistentStorageUsage", | 
| -            event.usage); | 
| -      } else { | 
| -        // We can't use the quota in the event because it assumes unlimited | 
| -        // storage. | 
| -        BrowserThread::PostTask( | 
| -            BrowserThread::IO, FROM_HERE, | 
| -            base::BindOnce(&LogTemporaryStorageUsage, state.quota_manager, | 
| -                           event.usage)); | 
| -      } | 
| +void SingleExtensionStorageObserver::OnStorageEvent(const Event& event) { | 
| +  if (should_uma_) { | 
| +    if (event.filter.storage_type == storage::kStorageTypePersistent) { | 
| +      UMA_HISTOGRAM_MEMORY_KB( | 
| +          "Extensions.HostedAppUnlimitedStoragePersistentStorageUsage", | 
| +          event.usage); | 
| +    } else { | 
| +      // We can't use the quota in the event because it assumes unlimited | 
| +      // storage. | 
| +      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 
| +                              base::BindOnce(&LogTemporaryStorageUsage, | 
| +                                             quota_manager_, event.usage)); | 
| } | 
| +  } | 
|  | 
| -    if (state.next_threshold != -1 && | 
| -        event.usage >= state.next_threshold) { | 
| -      while (event.usage >= state.next_threshold) | 
| -        state.next_threshold *= 2; | 
| +  if (next_threshold_ != -1 && event.usage >= next_threshold_) { | 
| +    while (event.usage >= next_threshold_) | 
| +      next_threshold_ *= 2; | 
|  | 
| -      BrowserThread::PostTask( | 
| -          BrowserThread::UI, FROM_HERE, | 
| -          base::BindOnce(&ExtensionStorageMonitor::OnStorageThresholdExceeded, | 
| -                         storage_monitor_, state.extension_id, | 
| -                         state.next_threshold, event.usage)); | 
| -    } | 
| +    BrowserThread::PostTask( | 
| +        BrowserThread::UI, FROM_HERE, | 
| +        base::BindOnce(&ExtensionStorageMonitor::OnStorageThresholdExceeded, | 
| +                       io_helper_->extension_storage_monitor(), extension_id_, | 
| +                       next_threshold_, event.usage)); | 
| } | 
| - | 
| -  OriginStorageStateMap origin_state_map_; | 
| -  base::WeakPtr<ExtensionStorageMonitor> storage_monitor_; | 
| -}; | 
| +} | 
|  | 
| // ExtensionStorageMonitor | 
|  | 
| @@ -270,7 +280,7 @@ ExtensionStorageMonitor::ExtensionStorageMonitor( | 
| content::BrowserContext* context) | 
| : enable_for_all_extensions_(false), | 
| initial_extension_threshold_(kExtensionInitialThreshold), | 
| -      observer_rate_(base::TimeDelta::FromSeconds(kStorageEventRateSec)), | 
| +      observer_rate_(kStorageEventRate), | 
| context_(context), | 
| extension_prefs_(ExtensionPrefs::Get(context)), | 
| extension_registry_observer_(this), | 
| @@ -330,12 +340,12 @@ void ExtensionStorageMonitor::OnExtensionWillBeInstalled( | 
| // higher than this, leave it as is. | 
| SetNextStorageThreshold(extension->id(), 0); | 
|  | 
| -    if (storage_observer_.get()) { | 
| +    if (io_helper_) { | 
| BrowserThread::PostTask( | 
| BrowserThread::IO, FROM_HERE, | 
| -          base::BindOnce(&StorageEventObserver::UpdateThresholdForExtension, | 
| -                         storage_observer_, extension->id(), | 
| -                         initial_extension_threshold_)); | 
| +          base::BindOnce( | 
| +              &ExtensionStorageMonitorIOHelper::UpdateThresholdForExtension, | 
| +              io_helper_, extension->id(), initial_extension_threshold_)); | 
| } | 
| } | 
| } | 
| @@ -485,9 +495,9 @@ void ExtensionStorageMonitor::StartMonitoringStorage( | 
| return;  // Don't track this extension. | 
|  | 
| // Lazily create the storage monitor proxy on the IO thread. | 
| -  if (!storage_observer_.get()) { | 
| -    storage_observer_ = | 
| -        new StorageEventObserver(weak_ptr_factory_.GetWeakPtr()); | 
| +  if (!io_helper_) { | 
| +    io_helper_ = base::MakeRefCounted<ExtensionStorageMonitorIOHelper>( | 
| +        weak_ptr_factory_.GetWeakPtr()); | 
| } | 
|  | 
| GURL site_url = util::GetSiteForExtensionId(extension->id(), context_); | 
| @@ -507,21 +517,22 @@ void ExtensionStorageMonitor::StartMonitoringStorage( | 
|  | 
| BrowserThread::PostTask( | 
| BrowserThread::IO, FROM_HERE, | 
| -      base::BindOnce(&StorageEventObserver::StartObservingForExtension, | 
| -                     storage_observer_, quota_manager, extension->id(), | 
| -                     storage_origin, next_threshold, observer_rate_, | 
| -                     for_metrics)); | 
| +      base::BindOnce( | 
| +          &ExtensionStorageMonitorIOHelper::StartObservingForExtension, | 
| +          io_helper_, quota_manager, extension->id(), storage_origin, | 
| +          next_threshold, observer_rate_, for_metrics)); | 
| } | 
|  | 
| void ExtensionStorageMonitor::StopMonitoringStorage( | 
| const std::string& extension_id) { | 
| -  if (!storage_observer_.get()) | 
| +  if (!io_helper_.get()) | 
| return; | 
|  | 
| BrowserThread::PostTask( | 
| BrowserThread::IO, FROM_HERE, | 
| -      base::BindOnce(&StorageEventObserver::StopObservingForExtension, | 
| -                     storage_observer_, extension_id)); | 
| +      base::BindOnce( | 
| +          &ExtensionStorageMonitorIOHelper::StopObservingForExtension, | 
| +          io_helper_, extension_id)); | 
| } | 
|  | 
| void ExtensionStorageMonitor::StopMonitoringAll() { | 
| @@ -529,13 +540,8 @@ void ExtensionStorageMonitor::StopMonitoringAll() { | 
|  | 
| RemoveAllNotifications(); | 
|  | 
| -  if (!storage_observer_.get()) | 
| -    return; | 
| - | 
| -  BrowserThread::PostTask( | 
| -      BrowserThread::IO, FROM_HERE, | 
| -      base::BindOnce(&StorageEventObserver::StopObserving, storage_observer_)); | 
| -  storage_observer_ = NULL; | 
| +  io_helper_ = nullptr; | 
| +  weak_ptr_factory_.InvalidateWeakPtrs(); | 
| } | 
|  | 
| void ExtensionStorageMonitor::RemoveNotificationForExtension( | 
|  |