Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_storage_monitor.cc
blob1e3f09fa0bb35e7a976dc56a5c7bac163ba79a84
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.
5 #include "chrome/browser/extensions/extension_storage_monitor.h"
7 #include <map>
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_storage_monitor_factory.h"
14 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/extensions/image_loader.h"
16 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/notification_details.h"
20 #include "content/public/browser/notification_source.h"
21 #include "content/public/browser/storage_partition.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/manifest_handlers/icons_handler.h"
26 #include "grit/generated_resources.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/message_center/message_center.h"
29 #include "ui/message_center/notifier_settings.h"
30 #include "ui/message_center/views/constants.h"
31 #include "webkit/browser/quota/quota_manager.h"
32 #include "webkit/browser/quota/storage_observer.h"
34 using content::BrowserThread;
36 namespace extensions {
38 namespace {
40 // The rate at which we would like to observe storage events.
41 const int kStorageEventRateSec = 30;
43 // The storage type to monitor.
44 const quota::StorageType kMonitorStorageType = quota::kStorageTypePersistent;
46 // Set the thresholds for the first notification. Ephemeral apps have a lower
47 // threshold than installed extensions and apps. Once a threshold is exceeded,
48 // it will be doubled to throttle notifications.
49 const int64 kMBytes = 1024 * 1024;
50 const int64 kEphemeralAppInitialThreshold = 250 * kMBytes;
51 const int64 kExtensionInitialThreshold = 1000 * kMBytes;
53 // Notifications have an ID so that we can update them.
54 const char kNotificationIdFormat[] = "ExtensionStorageMonitor-$1-$2";
55 const char kSystemNotifierId[] = "ExtensionStorageMonitor";
57 } // namespace
59 // 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
61 // UI thread, which displays the notification.
62 class StorageEventObserver
63 : public base::RefCountedThreadSafe<
64 StorageEventObserver,
65 BrowserThread::DeleteOnIOThread>,
66 public quota::StorageObserver {
67 public:
68 explicit StorageEventObserver(
69 base::WeakPtr<ExtensionStorageMonitor> storage_monitor)
70 : storage_monitor_(storage_monitor) {
73 // Register as an observer for the extension's storage events.
74 void StartObservingForExtension(
75 scoped_refptr<quota::QuotaManager> quota_manager,
76 const std::string& extension_id,
77 const GURL& site_url,
78 int64 next_threshold,
79 int rate) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
81 DCHECK(quota_manager.get());
83 GURL origin = site_url.GetOrigin();
84 StorageState& state = origin_state_map_[origin];
85 state.quota_manager = quota_manager;
86 state.extension_id = extension_id;
87 state.next_threshold = next_threshold;
89 quota::StorageObserver::MonitorParams params(
90 kMonitorStorageType,
91 origin,
92 base::TimeDelta::FromSeconds(rate),
93 false);
94 quota_manager->AddStorageObserver(this, params);
97 // Deregister as an observer for the extension's storage events.
98 void StopObservingForExtension(const std::string& extension_id) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
101 for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
102 it != origin_state_map_.end(); ) {
103 if (it->second.extension_id == extension_id) {
104 quota::StorageObserver::Filter filter(kMonitorStorageType, it->first);
105 it->second.quota_manager->RemoveStorageObserverForFilter(this, filter);
106 origin_state_map_.erase(it++);
107 } else {
108 ++it;
113 // Stop observing all storage events. Called during shutdown.
114 void StopObserving() {
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117 for (OriginStorageStateMap::iterator it = origin_state_map_.begin();
118 it != origin_state_map_.end(); ++it) {
119 it->second.quota_manager->RemoveStorageObserver(this);
121 origin_state_map_.clear();
124 private:
125 friend class base::DeleteHelper<StorageEventObserver>;
126 friend struct content::BrowserThread::DeleteOnThread<
127 content::BrowserThread::IO>;
129 struct StorageState {
130 scoped_refptr<quota::QuotaManager> quota_manager;
131 std::string extension_id;
132 int64 next_threshold;
134 StorageState() : next_threshold(0) {}
136 typedef std::map<GURL, StorageState> OriginStorageStateMap;
138 virtual ~StorageEventObserver() {
139 DCHECK(origin_state_map_.empty());
140 StopObserving();
143 // quota::StorageObserver implementation.
144 virtual void OnStorageEvent(const Event& event) OVERRIDE {
145 OriginStorageStateMap::iterator state =
146 origin_state_map_.find(event.filter.origin);
147 if (state == origin_state_map_.end())
148 return;
150 if (event.usage >= state->second.next_threshold) {
151 while (event.usage >= state->second.next_threshold)
152 state->second.next_threshold *= 2;
154 BrowserThread::PostTask(
155 BrowserThread::UI,
156 FROM_HERE,
157 base::Bind(&ExtensionStorageMonitor::OnStorageThresholdExceeded,
158 storage_monitor_,
159 state->second.extension_id,
160 state->second.next_threshold,
161 event.usage));
165 OriginStorageStateMap origin_state_map_;
166 base::WeakPtr<ExtensionStorageMonitor> storage_monitor_;
169 // ExtensionStorageMonitor
171 // static
172 ExtensionStorageMonitor* ExtensionStorageMonitor::Get(
173 content::BrowserContext* context) {
174 return ExtensionStorageMonitorFactory::GetForBrowserContext(context);
177 ExtensionStorageMonitor::ExtensionStorageMonitor(
178 content::BrowserContext* context)
179 : enable_for_all_extensions_(false),
180 initial_extension_threshold_(kExtensionInitialThreshold),
181 initial_ephemeral_threshold_(kEphemeralAppInitialThreshold),
182 observer_rate_(kStorageEventRateSec),
183 context_(context),
184 weak_ptr_factory_(this) {
185 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
186 content::Source<content::BrowserContext>(context_));
187 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
188 content::Source<content::BrowserContext>(context_));
190 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
191 DCHECK(registry);
192 registry->AddObserver(this);
195 ExtensionStorageMonitor::~ExtensionStorageMonitor() {}
197 void ExtensionStorageMonitor::Observe(
198 int type,
199 const content::NotificationSource& source,
200 const content::NotificationDetails& details) {
201 switch (type) {
202 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
203 const Extension* extension =
204 content::Details<const Extension>(details).ptr();
205 RemoveNotificationForExtension(extension->id());
206 break;
208 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
209 StopMonitoringAll();
210 break;
212 default:
213 NOTREACHED();
217 void ExtensionStorageMonitor::OnExtensionLoaded(
218 content::BrowserContext* browser_context,
219 const Extension* extension) {
220 DCHECK(extension);
221 StartMonitoringStorage(extension);
224 void ExtensionStorageMonitor::OnExtensionUnloaded(
225 content::BrowserContext* browser_context,
226 const Extension* extension,
227 UnloadedExtensionInfo::Reason reason) {
228 DCHECK(extension);
229 StopMonitoringStorage(extension->id());
232 std::string ExtensionStorageMonitor::GetNotificationId(
233 const std::string& extension_id) {
234 std::vector<std::string> placeholders;
235 placeholders.push_back(context_->GetPath().BaseName().MaybeAsASCII());
236 placeholders.push_back(extension_id);
238 return ReplaceStringPlaceholders(kNotificationIdFormat, placeholders, NULL);
241 void ExtensionStorageMonitor::OnStorageThresholdExceeded(
242 const std::string& extension_id,
243 int64 next_threshold,
244 int64 current_usage) {
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247 const Extension* extension = ExtensionRegistry::Get(context_)->
248 GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
249 if (!extension)
250 return;
252 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
253 DCHECK(prefs);
254 prefs->SetNextStorageThreshold(extension->id(), next_threshold);
256 const int kIconSize = message_center::kNotificationIconSize;
257 ExtensionResource resource = IconsInfo::GetIconResource(
258 extension, kIconSize, ExtensionIconSet::MATCH_BIGGER);
259 ImageLoader::Get(context_)->LoadImageAsync(
260 extension, resource, gfx::Size(kIconSize, kIconSize),
261 base::Bind(&ExtensionStorageMonitor::OnImageLoaded,
262 weak_ptr_factory_.GetWeakPtr(),
263 extension_id,
264 current_usage));
267 void ExtensionStorageMonitor::OnImageLoaded(
268 const std::string& extension_id,
269 int64 current_usage,
270 const gfx::Image& image) {
271 const Extension* extension = ExtensionRegistry::Get(context_)->
272 GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
273 if (!extension)
274 return;
276 // Remove any existing notifications to force a new notification to pop up.
277 std::string notification_id(GetNotificationId(extension_id));
278 message_center::MessageCenter::Get()->RemoveNotification(
279 notification_id, false);
281 message_center::RichNotificationData notification_data;
282 notification_data.buttons.push_back(message_center::ButtonInfo(
283 l10n_util::GetStringUTF16(extension->is_app() ?
284 IDS_EXTENSION_STORAGE_MONITOR_BUTTON_DISMISS_APP :
285 IDS_EXTENSION_STORAGE_MONITOR_BUTTON_DISMISS_EXTENSION)));
287 gfx::Image notification_image(image);
288 if (notification_image.IsEmpty()) {
289 notification_image =
290 extension->is_app() ? gfx::Image(util::GetDefaultAppIcon())
291 : gfx::Image(util::GetDefaultExtensionIcon());
294 scoped_ptr<message_center::Notification> notification;
295 notification.reset(new message_center::Notification(
296 message_center::NOTIFICATION_TYPE_SIMPLE,
297 notification_id,
298 l10n_util::GetStringUTF16(IDS_EXTENSION_STORAGE_MONITOR_TITLE),
299 l10n_util::GetStringFUTF16(
300 IDS_EXTENSION_STORAGE_MONITOR_TEXT,
301 base::UTF8ToUTF16(extension->name()),
302 base::IntToString16(current_usage / kMBytes)),
303 notification_image,
304 base::string16() /* display source */,
305 message_center::NotifierId(
306 message_center::NotifierId::SYSTEM_COMPONENT, kSystemNotifierId),
307 notification_data,
308 new message_center::HandleNotificationButtonClickDelegate(base::Bind(
309 &ExtensionStorageMonitor::OnNotificationButtonClick,
310 weak_ptr_factory_.GetWeakPtr(),
311 extension_id))));
312 notification->SetSystemPriority();
313 message_center::MessageCenter::Get()->AddNotification(notification.Pass());
315 notified_extension_ids_.insert(extension_id);
318 void ExtensionStorageMonitor::OnNotificationButtonClick(
319 const std::string& extension_id, int button_index) {
320 switch (button_index) {
321 case BUTTON_DISABLE_NOTIFICATION: {
322 DisableStorageMonitoring(extension_id);
323 break;
325 default:
326 NOTREACHED();
330 void ExtensionStorageMonitor::DisableStorageMonitoring(
331 const std::string& extension_id) {
332 StopMonitoringStorage(extension_id);
334 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
335 DCHECK(prefs);
336 prefs->SetStorageNotificationEnabled(extension_id, false);
338 message_center::MessageCenter::Get()->RemoveNotification(
339 GetNotificationId(extension_id), false);
342 void ExtensionStorageMonitor::StartMonitoringStorage(
343 const Extension* extension) {
344 if (!extension->HasAPIPermission(APIPermission::kUnlimitedStorage))
345 return;
347 // Do not monitor storage for component extensions.
348 if (extension->location() == Manifest::COMPONENT)
349 return;
351 // First apply this feature only to experimental ephemeral apps. If it works
352 // well, roll it out to all extensions and apps.
353 if (!extension->is_ephemeral() && !enable_for_all_extensions_)
354 return;
356 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
357 DCHECK(prefs);
358 if (!prefs->IsStorageNotificationEnabled(extension->id()))
359 return;
361 // Lazily create the storage monitor proxy on the IO thread.
362 if (!storage_observer_.get()) {
363 storage_observer_ =
364 new StorageEventObserver(weak_ptr_factory_.GetWeakPtr());
367 GURL site_url =
368 extensions::util::GetSiteForExtensionId(extension->id(), context_);
369 content::StoragePartition* storage_partition =
370 content::BrowserContext::GetStoragePartitionForSite(context_, site_url);
371 DCHECK(storage_partition);
372 scoped_refptr<quota::QuotaManager> quota_manager(
373 storage_partition->GetQuotaManager());
375 GURL storage_origin(site_url.GetOrigin());
376 if (extension->is_hosted_app())
377 storage_origin = AppLaunchInfo::GetLaunchWebURL(extension).GetOrigin();
379 int next_threshold = prefs->GetNextStorageThreshold(extension->id());
380 if (next_threshold == 0) {
381 // The next threshold is written to the prefs after the initial threshold is
382 // exceeded.
383 next_threshold = extension->is_ephemeral() ? initial_ephemeral_threshold_
384 : initial_extension_threshold_;
387 BrowserThread::PostTask(
388 BrowserThread::IO,
389 FROM_HERE,
390 base::Bind(&StorageEventObserver::StartObservingForExtension,
391 storage_observer_,
392 quota_manager,
393 extension->id(),
394 storage_origin,
395 next_threshold,
396 observer_rate_));
399 void ExtensionStorageMonitor::StopMonitoringStorage(
400 const std::string& extension_id) {
401 if (!storage_observer_.get())
402 return;
404 BrowserThread::PostTask(
405 BrowserThread::IO,
406 FROM_HERE,
407 base::Bind(&StorageEventObserver::StopObservingForExtension,
408 storage_observer_,
409 extension_id));
412 void ExtensionStorageMonitor::StopMonitoringAll() {
413 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
414 DCHECK(registry);
415 registry->RemoveObserver(this);
417 RemoveAllNotifications();
419 if (!storage_observer_.get())
420 return;
422 BrowserThread::PostTask(
423 BrowserThread::IO,
424 FROM_HERE,
425 base::Bind(&StorageEventObserver::StopObserving, storage_observer_));
426 storage_observer_ = NULL;
429 void ExtensionStorageMonitor::RemoveNotificationForExtension(
430 const std::string& extension_id) {
431 std::set<std::string>::iterator ext_id =
432 notified_extension_ids_.find(extension_id);
433 if (ext_id == notified_extension_ids_.end())
434 return;
436 notified_extension_ids_.erase(ext_id);
437 message_center::MessageCenter::Get()->RemoveNotification(
438 GetNotificationId(extension_id), false);
441 void ExtensionStorageMonitor::RemoveAllNotifications() {
442 if (notified_extension_ids_.empty())
443 return;
445 message_center::MessageCenter* center = message_center::MessageCenter::Get();
446 DCHECK(center);
447 for (std::set<std::string>::iterator it = notified_extension_ids_.begin();
448 it != notified_extension_ids_.end(); ++it) {
449 center->RemoveNotification(GetNotificationId(*it), false);
451 notified_extension_ids_.clear();
454 } // namespace extensions