Gather metrics on hosted app unlimitedStorage usage

BUG=416691

Review URL: https://codereview.chromium.org/612983004

Cr-Commit-Position: refs/heads/master@{#297323}
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 3f1b472e..ec96a31 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -1008,7 +1008,7 @@
   // TODO(kalman): Convert ExtensionSpecialStoragePolicy to a
   // BrowserContextKeyedService and use ExtensionRegistryObserver.
   profile_->GetExtensionSpecialStoragePolicy()->
-      GrantRightsForExtension(extension);
+      GrantRightsForExtension(extension, profile_);
 
   // TODO(kalman): This is broken. The crash reporter is process-wide so doesn't
   // work properly multi-profile. Besides which, it should be using
diff --git a/chrome/browser/extensions/extension_special_storage_policy.cc b/chrome/browser/extensions/extension_special_storage_policy.cc
index c0e5be9..3ee4aef 100644
--- a/chrome/browser/extensions/extension_special_storage_policy.cc
+++ b/chrome/browser/extensions/extension_special_storage_policy.cc
@@ -7,26 +7,73 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/content_settings/cookie_settings.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/url_constants.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "storage/browser/quota/quota_manager.h"
+#include "storage/common/quota/quota_status_code.h"
+#include "storage/common/quota/quota_types.h"
 
 using content::BrowserThread;
 using extensions::APIPermission;
 using extensions::Extension;
 using storage::SpecialStoragePolicy;
 
+namespace {
+
+void ReportQuotaUsage(storage::QuotaStatusCode code, int64 usage, int64 quota) {
+  if (code == storage::kQuotaStatusOk) {
+    // We're interested in the amount of space hosted apps are using. Record it
+    // when the extension is granted the unlimited storage permission (once per
+    // extension load, so on average once per run).
+    UMA_HISTOGRAM_MEMORY_KB("Extensions.HostedAppUnlimitedStorageUsage", usage);
+  }
+}
+
+// Log the usage for a hosted app with unlimited storage.
+void LogHostedAppUnlimitedStorageUsage(
+    scoped_refptr<const Extension> extension,
+    content::BrowserContext* browser_context) {
+  GURL launch_url =
+      extensions::AppLaunchInfo::GetLaunchWebURL(extension.get()).GetOrigin();
+  content::StoragePartition* partition =
+      browser_context ?  // |browser_context| can be NULL in unittests.
+      content::BrowserContext::GetStoragePartitionForSite(browser_context,
+                                                          launch_url) :
+      NULL;
+  if (partition) {
+    // We only have to query for kStorageTypePersistent data usage, because apps
+    // cannot ask for any more temporary storage, according to
+    // https://developers.google.com/chrome/whitepapers/storage.
+    BrowserThread::PostTask(
+        BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&storage::QuotaManager::GetUsageAndQuotaForWebApps,
+                   partition->GetQuotaManager(),
+                   launch_url,
+                   storage::kStorageTypePersistent,
+                   base::Bind(&ReportQuotaUsage)));
+  }
+}
+
+}  // namespace
+
 ExtensionSpecialStoragePolicy::ExtensionSpecialStoragePolicy(
     CookieSettings* cookie_settings)
     : cookie_settings_(cookie_settings) {}
@@ -101,7 +148,8 @@
 }
 
 void ExtensionSpecialStoragePolicy::GrantRightsForExtension(
-    const extensions::Extension* extension) {
+    const extensions::Extension* extension,
+    content::BrowserContext* browser_context) {
   DCHECK(extension);
   if (!(NeedsProtection(extension) ||
         extension->permissions_data()->HasAPIPermission(
@@ -124,8 +172,12 @@
 
     if (extension->permissions_data()->HasAPIPermission(
             APIPermission::kUnlimitedStorage) &&
-        unlimited_extensions_.Add(extension))
+        unlimited_extensions_.Add(extension)) {
+      if (extension->is_hosted_app())
+        LogHostedAppUnlimitedStorageUsage(extension, browser_context);
+
       change_flags |= SpecialStoragePolicy::STORAGE_UNLIMITED;
+    }
 
     if (extension->permissions_data()->HasAPIPermission(
             APIPermission::kFileBrowserHandler))
diff --git a/chrome/browser/extensions/extension_special_storage_policy.h b/chrome/browser/extensions/extension_special_storage_policy.h
index eec7577..fda9be6 100644
--- a/chrome/browser/extensions/extension_special_storage_policy.h
+++ b/chrome/browser/extensions/extension_special_storage_policy.h
@@ -15,6 +15,10 @@
 
 class CookieSettings;
 
+namespace content {
+class BrowserContext;
+}
+
 namespace extensions {
 class Extension;
 }
@@ -37,7 +41,8 @@
   virtual bool HasSessionOnlyOrigins() OVERRIDE;
 
   // Methods used by the ExtensionService to populate this class.
-  void GrantRightsForExtension(const extensions::Extension* extension);
+  void GrantRightsForExtension(const extensions::Extension* extension,
+                               content::BrowserContext* browser_context);
   void RevokeRightsForExtension(const extensions::Extension* extension);
   void RevokeRightsForAllExtensions();
 
diff --git a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
index 06a5d33..09984c2 100644
--- a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
+++ b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
@@ -198,7 +198,7 @@
 
 TEST_F(ExtensionSpecialStoragePolicyTest, AppWithProtectedStorage) {
   scoped_refptr<Extension> extension(CreateProtectedApp());
-  policy_->GrantRightsForExtension(extension.get());
+  policy_->GrantRightsForExtension(extension.get(), NULL);
   ExtensionSet protecting_extensions;
   protecting_extensions.Insert(extension);
   ExtensionSet empty_set;
@@ -219,7 +219,7 @@
 
 TEST_F(ExtensionSpecialStoragePolicyTest, AppWithUnlimitedStorage) {
   scoped_refptr<Extension> extension(CreateUnlimitedApp());
-  policy_->GrantRightsForExtension(extension.get());
+  policy_->GrantRightsForExtension(extension.get(), NULL);
   ExtensionSet protecting_extensions;
   protecting_extensions.Insert(extension);
   ExtensionSet empty_set;
@@ -253,9 +253,9 @@
   scoped_refptr<Extension> regular_app(CreateRegularApp());
   scoped_refptr<Extension> protected_app(CreateProtectedApp());
   scoped_refptr<Extension> unlimited_app(CreateUnlimitedApp());
-  policy_->GrantRightsForExtension(regular_app.get());
-  policy_->GrantRightsForExtension(protected_app.get());
-  policy_->GrantRightsForExtension(unlimited_app.get());
+  policy_->GrantRightsForExtension(regular_app.get(), NULL);
+  policy_->GrantRightsForExtension(protected_app.get(), NULL);
+  policy_->GrantRightsForExtension(unlimited_app.get(), NULL);
 
   EXPECT_FALSE(policy_->CanQueryDiskSize(kHttpUrl));
   EXPECT_FALSE(policy_->CanQueryDiskSize(kExtensionUrl));
@@ -268,7 +268,7 @@
   const GURL kHttpUrl("http://foo");
   const GURL kExtensionUrl("chrome-extension://bar");
   scoped_refptr<Extension> app(CreateRegularApp());
-  policy_->GrantRightsForExtension(app.get());
+  policy_->GrantRightsForExtension(app.get(), NULL);
 
   EXPECT_FALSE(policy_->HasIsolatedStorage(kHttpUrl));
   EXPECT_FALSE(policy_->HasIsolatedStorage(kExtensionUrl));
@@ -278,8 +278,8 @@
 TEST_F(ExtensionSpecialStoragePolicyTest, OverlappingApps) {
   scoped_refptr<Extension> protected_app(CreateProtectedApp());
   scoped_refptr<Extension> unlimited_app(CreateUnlimitedApp());
-  policy_->GrantRightsForExtension(protected_app.get());
-  policy_->GrantRightsForExtension(unlimited_app.get());
+  policy_->GrantRightsForExtension(protected_app.get(), NULL);
+  policy_->GrantRightsForExtension(unlimited_app.get(), NULL);
   ExtensionSet protecting_extensions;
   ExtensionSet empty_set;
   protecting_extensions.Insert(protected_app);
@@ -371,14 +371,14 @@
   for (size_t i = 0; i < arraysize(apps); ++i) {
     SCOPED_TRACE(testing::Message() << "i: " << i);
     observer.ExpectGrant(apps[i]->id(), change_flags[i]);
-    policy_->GrantRightsForExtension(apps[i].get());
+    policy_->GrantRightsForExtension(apps[i].get(), NULL);
     message_loop.RunUntilIdle();
     EXPECT_TRUE(observer.IsCompleted());
   }
 
   for (size_t i = 0; i < arraysize(apps); ++i) {
     SCOPED_TRACE(testing::Message() << "i: " << i);
-    policy_->GrantRightsForExtension(apps[i].get());
+    policy_->GrantRightsForExtension(apps[i].get(), NULL);
     message_loop.RunUntilIdle();
     EXPECT_TRUE(observer.IsCompleted());
   }