Record scheme of URLs that set Secure cookies

This CL histograms the type of scheme (cryptographic or not
cryptographic) for URLs that set or overwrite Secure cookies. The goal
is to measure whether we can remove the ability for http:// URLs to set
Secure cookies.

BUG=522261

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

Cr-Commit-Position: refs/heads/master@{#344907}
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 22539c7..2292f8c 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -1826,10 +1826,31 @@
   }
 
   // See InitializeHistograms() for details.
-  int32_t sample = cc->IsFirstPartyOnly() ? 1 << COOKIE_TYPE_FIRSTPARTYONLY : 0;
-  sample |= cc->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0;
-  sample |= cc->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0;
-  histogram_cookie_type_->Add(sample);
+  int32_t cookie_type_sample =
+      cc->IsFirstPartyOnly() ? 1 << COOKIE_TYPE_FIRSTPARTYONLY : 0;
+  cookie_type_sample |= cc->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0;
+  cookie_type_sample |= cc->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0;
+  histogram_cookie_type_->Add(cookie_type_sample);
+
+  // Histogram the type of scheme used on URLs that set cookies. This
+  // intentionally includes cookies that are set or overwritten by
+  // http:// URLs, but not cookies that are cleared by http:// URLs, to
+  // understand if the former behavior can be deprecated for Secure
+  // cookies.
+  if (!cc->Source().is_empty()) {
+    CookieSource cookie_source_sample;
+    if (cc->Source().SchemeIsCryptographic()) {
+      cookie_source_sample =
+          cc->IsSecure() ? COOKIE_SOURCE_SECURE_COOKIE_CRYPTOGRAPHIC_SCHEME
+                         : COOKIE_SOURCE_NONSECURE_COOKIE_CRYPTOGRAPHIC_SCHEME;
+    } else {
+      cookie_source_sample =
+          cc->IsSecure()
+              ? COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME
+              : COOKIE_SOURCE_NONSECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME;
+    }
+    histogram_cookie_source_scheme_->Add(cookie_source_sample);
+  }
 
   RunCallbacks(*cc, false);
 
@@ -2239,6 +2260,9 @@
   histogram_cookie_type_ = base::LinearHistogram::FactoryGet(
       "Cookie.Type", 1, (1 << COOKIE_TYPE_LAST_ENTRY) - 1,
       1 << COOKIE_TYPE_LAST_ENTRY, base::Histogram::kUmaTargetedHistogramFlag);
+  histogram_cookie_source_scheme_ = base::LinearHistogram::FactoryGet(
+      "Cookie.CookieSourceScheme", 1, COOKIE_SOURCE_LAST_ENTRY - 1,
+      COOKIE_SOURCE_LAST_ENTRY, base::Histogram::kUmaTargetedHistogramFlag);
 
   // From UMA_HISTOGRAM_{CUSTOM_,}TIMES
   histogram_time_blocked_on_load_ = base::Histogram::FactoryTimeGet(
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 0367343..ced1c23 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -360,6 +360,9 @@
   // For ComputeCookieDiff.
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, ComputeCookieDiff);
 
+  // For CookieSource histogram enum.
+  FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest, CookieSourceHistogram);
+
   // Internal reasons for deletion, used to populate informative histograms
   // and to provide a public cause for onCookieChange notifications.
   //
@@ -410,6 +413,26 @@
     COOKIE_TYPE_LAST_ENTRY
   };
 
+  // Used to populate a histogram containing information about the
+  // sources of Secure and non-Secure cookies: that is, whether such
+  // cookies are set by origins with cryptographic or non-cryptographic
+  // schemes. Please do not reorder the list when adding new
+  // entries. New items MUST be added at the end of the list, just
+  // before COOKIE_SOURCE_LAST_ENTRY.
+  //
+  // COOKIE_SOURCE_(NON)SECURE_COOKIE_(NON)CRYPTOGRAPHIC_SCHEME means
+  // that a cookie was set or overwritten from a URL with the given type
+  // of scheme. This enum should not be used when cookies are *cleared*,
+  // because its purpose is to understand if Chrome can deprecate the
+  // ability of HTTP urls to set/overwrite Secure cookies.
+  enum CookieSource {
+    COOKIE_SOURCE_SECURE_COOKIE_CRYPTOGRAPHIC_SCHEME = 0,
+    COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME,
+    COOKIE_SOURCE_NONSECURE_COOKIE_CRYPTOGRAPHIC_SCHEME,
+    COOKIE_SOURCE_NONSECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME,
+    COOKIE_SOURCE_LAST_ENTRY
+  };
+
   // The strategy for fetching cookies. Controlled by Finch experiment.
   enum FetchStrategy {
     // Fetches all cookies only when they're needed.
@@ -658,6 +681,7 @@
   base::HistogramBase* histogram_count_;
   base::HistogramBase* histogram_cookie_deletion_cause_;
   base::HistogramBase* histogram_cookie_type_;
+  base::HistogramBase* histogram_cookie_source_scheme_;
   base::HistogramBase* histogram_time_blocked_on_load_;
 
   CookieMap cookies_;
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 6f3c0b61..ea9b13cf 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/histogram_tester.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
@@ -2811,6 +2812,68 @@
   EXPECT_EQ("foo=bar; hello=world", GetCookies(cm.get(), url));
 }
 
+// Test that cookie source schemes are histogrammed correctly.
+TEST_F(CookieMonsterTest, CookieSourceHistogram) {
+  base::HistogramTester histograms;
+  const std::string cookie_source_histogram = "Cookie.CookieSourceScheme";
+
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
+  scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL));
+
+  histograms.ExpectTotalCount(cookie_source_histogram, 0);
+
+  // Set a Secure cookie on a cryptographic scheme.
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_secure_, "A=B; path=/; Secure"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 1);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_CRYPTOGRAPHIC_SCHEME, 1);
+
+  // Set a non-Secure cookie on a cryptographic scheme.
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_secure_, "C=D; path=/;"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 2);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_SOURCE_NONSECURE_COOKIE_CRYPTOGRAPHIC_SCHEME, 1);
+
+  // Set a Secure cookie on a non-cryptographic scheme.
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_, "D=E; path=/; Secure"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 3);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 1);
+
+  // Overwrite a Secure cookie (set by a cryptographic scheme) on a
+  // non-cryptographic scheme.
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_, "A=B; path=/; Secure"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 4);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_CRYPTOGRAPHIC_SCHEME, 1);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_SOURCE_SECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 2);
+
+  // Test that clearing a Secure cookie on a http:// URL does not get
+  // counted.
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_secure_, "F=G; path=/; Secure"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 5);
+  std::string cookies1 = GetCookies(cm.get(), url_google_secure_);
+  EXPECT_NE(std::string::npos, cookies1.find("F=G"));
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_,
+                        "F=G; path=/; Expires=Thu, 01-Jan-1970 00:00:01 GMT"));
+  std::string cookies2 = GetCookies(cm.get(), url_google_secure_);
+  EXPECT_EQ(std::string::npos, cookies2.find("F=G"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 5);
+
+  // Set a non-Secure cookie on a non-cryptographic scheme.
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_, "H=I; path=/"));
+  histograms.ExpectTotalCount(cookie_source_histogram, 6);
+  histograms.ExpectBucketCount(
+      cookie_source_histogram,
+      CookieMonster::COOKIE_SOURCE_NONSECURE_COOKIE_NONCRYPTOGRAPHIC_SCHEME, 1);
+}
+
 class CookieMonsterNotificationTest : public CookieMonsterTest {
  public:
   CookieMonsterNotificationTest()
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7ca956de..2fd2301 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4422,6 +4422,14 @@
   <summary>Intervals between access time updates for each cookie.</summary>
 </histogram>
 
+<histogram name="Cookie.CookieSourceScheme" enum="CookieSourceScheme">
+  <owner>[email protected]</owner>
+  <summary>
+    For each cookie added to the store, record whether its source URL has a
+    cryptographic scheme, broken down by Secure and not Secure.
+  </summary>
+</histogram>
+
 <histogram name="Cookie.CorruptMetaTable">
   <owner>[email protected]</owner>
   <summary>
@@ -54233,6 +54241,13 @@
   <int value="3" label="Both"/>
 </enum>
 
+<enum name="CookieSourceScheme" type="int">
+  <int value="0" label="Secure cookie, source scheme is cryptographic"/>
+  <int value="1" label="Secure cookie, source scheme is not cryptographic"/>
+  <int value="2" label="Not Secure cookie, source scheme is cryptographic"/>
+  <int value="3" label="Not Secure cookie, source scheme is not cryptographic"/>
+</enum>
+
 <enum name="CookieType" type="int">
   <summary>The type of a cookie when its added to the cookie store.</summary>
   <int value="0" label="Default"/>