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 "ios/net/cookies/system_cookie_util.h"
7 #import <Foundation/Foundation.h>
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "net/cookies/cookie_constants.h"
20 // Used to report cookie loss events in UMA.
23 NOT_ENOUGH_COOKIES, // Not enough cookies, error checking is disabled.
25 COOKIE_LOSS_ENUM_COUNT
28 // Undocumented property of NSHTTPCookie.
29 NSString* const kNSHTTPCookieHttpOnly = @"HttpOnly";
31 // Key in NSUserDefaults telling wether a low cookie count must be reported as
33 NSString* const kCheckCookieLossKey = @"CookieUtilIOSCheckCookieLoss";
35 // When the cookie count reaches |kCookieThresholdHigh|, and then drops below
36 // |kCookieThresholdLow|, an error is reported.
37 // Cookies count immediately after installation may fluctuate due to session
38 // cookies that can expire quickly. In order to catch true cookie loss, it is
39 // better to wait till a sufficiently large number of cookies have been
40 // accumulated before checking for sudden drop in cookies count.
41 const size_t kCookieThresholdHigh = 100;
42 const size_t kCookieThresholdLow = 30;
44 // Updates the cookie loss histograms.
45 // |event| determines which histogram is updated, and |loss| is the value to add
47 void ReportUMACookieLoss(CookieLossType loss, CookieEvent event) {
48 // Histogram macros require constant histogram names. Use the string constants
49 // explicitly instead of using a variable.
52 UMA_HISTOGRAM_ENUMERATION("CookieStoreIOS.LossOnRead", loss,
53 COOKIE_LOSS_ENUM_COUNT);
55 case COOKIES_APPLICATION_FOREGROUNDED:
56 UMA_HISTOGRAM_ENUMERATION("CookieStoreIOS.LossOnForegrounding", loss,
57 COOKIE_LOSS_ENUM_COUNT);
64 // Converts NSHTTPCookie to net::CanonicalCookie.
65 net::CanonicalCookie CanonicalCookieFromSystemCookie(
67 const base::Time& ceation_time) {
68 return net::CanonicalCookie(
69 GURL(), base::SysNSStringToUTF8([cookie name]),
70 base::SysNSStringToUTF8([cookie value]),
71 base::SysNSStringToUTF8([cookie domain]),
72 base::SysNSStringToUTF8([cookie path]), ceation_time,
73 base::Time::FromDoubleT([[cookie expiresDate] timeIntervalSince1970]),
74 base::Time(), [cookie isSecure], [cookie isHTTPOnly], false,
75 net::COOKIE_PRIORITY_DEFAULT);
78 // Converts net::CanonicalCookie to NSHTTPCookie.
79 NSHTTPCookie* SystemCookieFromCanonicalCookie(
80 const net::CanonicalCookie& cookie) {
81 NSString* cookie_domain = base::SysUTF8ToNSString(cookie.Domain());
82 NSString* cookie_name = base::SysUTF8ToNSString(cookie.Name());
83 NSString* cookie_path = base::SysUTF8ToNSString(cookie.Path());
84 NSString* cookie_value = base::SysUTF8ToNSString(cookie.Value());
85 if (!cookie_domain || !cookie_name || !cookie_path || !cookie_value) {
86 DLOG(ERROR) << "Cannot create system cookie: " << cookie.DebugString();
89 NSMutableDictionary* properties =
90 [NSMutableDictionary dictionaryWithDictionary:@{
91 NSHTTPCookieDomain : cookie_domain,
92 NSHTTPCookieName : cookie_name,
93 NSHTTPCookiePath : cookie_path,
94 NSHTTPCookieValue : cookie_value,
96 if (cookie.IsPersistent()) {
98 [NSDate dateWithTimeIntervalSince1970:cookie.ExpiryDate().ToDoubleT()];
99 [properties setObject:expiry forKey:NSHTTPCookieExpires];
101 if (cookie.IsSecure())
102 [properties setObject:@"Y" forKey:NSHTTPCookieSecure];
103 if (cookie.IsHttpOnly())
104 [properties setObject:@YES forKey:kNSHTTPCookieHttpOnly];
105 NSHTTPCookie* system_cookie = [NSHTTPCookie cookieWithProperties:properties];
106 DCHECK(system_cookie);
107 return system_cookie;
110 void CheckForCookieLoss(size_t cookie_count, CookieEvent event) {
111 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
112 BOOL check_cookie_loss = [defaults boolForKey:kCheckCookieLossKey];
114 // Start reporting errors if the cookie count goes above kCookieThresholdHigh.
115 if (!check_cookie_loss && cookie_count >= kCookieThresholdHigh) {
116 [defaults setBool:YES forKey:kCheckCookieLossKey];
117 [defaults synchronize];
121 if (!check_cookie_loss) {
122 // Error reporting is disabled.
123 ReportUMACookieLoss(NOT_ENOUGH_COOKIES, event);
127 if (cookie_count > kCookieThresholdLow) {
129 ReportUMACookieLoss(NO_LOSS, event);
134 // TODO(ios): [Merge 277884]: crbug.com/386074 ERROR_REPORT is no longer
136 LOG(ERROR_REPORT) << "Possible cookie issue (crbug/370024)\nCookie count: "
140 ReportUMACookieLoss(LOSS, event);
142 // Disable reporting until the cookie count rises above kCookieThresholdHigh
144 ResetCookieCountMetrics();
147 void ResetCookieCountMetrics() {
148 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
149 [defaults setBool:NO forKey:kCheckCookieLossKey];
150 [defaults synchronize];