Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "components/open_from_clipboard/clipboard_recent_content_ios.h" | 5 #import "components/open_from_clipboard/clipboard_recent_content_ios.h" |
| 6 | 6 |
| 7 #import <CommonCrypto/CommonDigest.h> | 7 #import <CommonCrypto/CommonDigest.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <stdint.h> | 9 #include <stdint.h> |
| 10 #import <UIKit/UIKit.h> | 10 #import <UIKit/UIKit.h> |
| 11 | 11 |
| 12 #import "base/ios/ios_util.h" | 12 #import "base/ios/ios_util.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/metrics/user_metrics.h" | 15 #include "base/metrics/user_metrics.h" |
| 16 #include "base/strings/sys_string_conversions.h" | 16 #include "base/strings/sys_string_conversions.h" |
| 17 #include "base/sys_info.h" | 17 #include "base/sys_info.h" |
| 18 #include "url/gurl.h" | 18 #include "url/gurl.h" |
| 19 #include "url/url_constants.h" | 19 #include "url/url_constants.h" |
| 20 | 20 |
| 21 // Bridge that forwards pasteboard change notifications to its delegate. | 21 // Bridge that forwards UIApplicationDidBecomeActiveNotification notifications |
| 22 @interface PasteboardNotificationListenerBridge : NSObject | 22 // to its delegate. |
| 23 @interface ApplicationDidBecomeActiveNotificationListenerBridge : NSObject | |
| 23 | 24 |
| 24 // Initialize the PasteboardNotificationListenerBridge with |delegate| which | 25 // Initialize the ApplicationDidBecomeActiveNotificationListenerBridge with |
| 25 // must not be null. | 26 // |delegate| which must not be null. |
| 26 - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate | 27 - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate |
| 27 NS_DESIGNATED_INITIALIZER; | 28 NS_DESIGNATED_INITIALIZER; |
| 28 | 29 |
| 29 - (instancetype)init NS_UNAVAILABLE; | 30 - (instancetype)init NS_UNAVAILABLE; |
| 30 | 31 |
| 31 @end | 32 @end |
| 32 | 33 |
| 33 @implementation PasteboardNotificationListenerBridge { | 34 @implementation ApplicationDidBecomeActiveNotificationListenerBridge { |
| 34 ClipboardRecentContentIOS* _delegate; | 35 ClipboardRecentContentIOS* _delegate; |
| 35 } | 36 } |
| 36 | 37 |
| 37 - (instancetype)init { | 38 - (instancetype)init { |
| 38 NOTREACHED(); | 39 NOTREACHED(); |
| 39 return nil; | 40 return nil; |
| 40 } | 41 } |
| 41 | 42 |
| 42 - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate { | 43 - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate { |
| 43 DCHECK(delegate); | 44 DCHECK(delegate); |
| 44 self = [super init]; | 45 self = [super init]; |
| 45 if (self) { | 46 if (self) { |
| 46 _delegate = delegate; | 47 _delegate = delegate; |
| 47 [[NSNotificationCenter defaultCenter] | 48 [[NSNotificationCenter defaultCenter] |
| 48 addObserver:self | 49 addObserver:self |
| 49 selector:@selector(pasteboardChangedNotification:) | |
| 50 name:UIPasteboardChangedNotification | |
| 51 object:[UIPasteboard generalPasteboard]]; | |
| 52 [[NSNotificationCenter defaultCenter] | |
| 53 addObserver:self | |
| 54 selector:@selector(didBecomeActive:) | 50 selector:@selector(didBecomeActive:) |
| 55 name:UIApplicationDidBecomeActiveNotification | 51 name:UIApplicationDidBecomeActiveNotification |
| 56 object:nil]; | 52 object:nil]; |
| 57 } | 53 } |
| 58 return self; | 54 return self; |
| 59 } | 55 } |
| 60 | 56 |
| 61 - (void)dealloc { | 57 - (void)dealloc { |
| 62 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 58 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 63 [super dealloc]; | 59 [super dealloc]; |
| 64 } | 60 } |
| 65 | 61 |
| 66 - (void)pasteboardChangedNotification:(NSNotification*)notification { | |
| 67 if (_delegate) { | |
| 68 _delegate->PasteboardChanged(); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 - (void)didBecomeActive:(NSNotification*)notification { | 62 - (void)didBecomeActive:(NSNotification*)notification { |
| 73 if (_delegate) { | 63 if (_delegate) { |
| 74 _delegate->LoadFromUserDefaults(); | 64 _delegate->LoadFromUserDefaults(); |
| 75 if (_delegate->HasPasteboardChanged(base::SysInfo::Uptime())) { | 65 _delegate->CheckIfPasteboardChanged(); |
| 76 _delegate->PasteboardChanged(); | |
| 77 } | |
| 78 } | 66 } |
| 79 } | 67 } |
| 80 | 68 |
| 81 - (void)disconnect { | 69 - (void)disconnect { |
| 82 _delegate = nullptr; | 70 _delegate = nullptr; |
| 83 } | 71 } |
| 84 | 72 |
| 85 @end | 73 @end |
| 86 | 74 |
| 87 namespace { | 75 namespace { |
| 88 // Key used to store the pasteboard's current change count. If when resuming | 76 // Key used to store the pasteboard's current change count. If when resuming |
| 89 // chrome the pasteboard's change count is different from the stored one, then | 77 // chrome the pasteboard's change count is different from the stored one, then |
| 90 // it means that the pasteboard's content has changed. | 78 // it means that the pasteboard's content has changed. |
| 91 NSString* kPasteboardChangeCountKey = @"PasteboardChangeCount"; | 79 NSString* kPasteboardChangeCountKey = @"PasteboardChangeCount"; |
| 92 // Key used to store the last date at which it was detected that the pasteboard | 80 // Key used to store the last date at which it was detected that the pasteboard |
| 93 // changed. It is used to evaluate the age of the pasteboard's content. | 81 // changed. It is used to evaluate the age of the pasteboard's content. |
| 94 NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate"; | 82 NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate"; |
| 95 // Key used to store the hash of the content of the pasteboard. Whenever the | 83 // Key used to store the hash of the content of the pasteboard. Whenever the |
| 96 // hash changed, the pasteboard content is considered to have changed. | 84 // hash changed, the pasteboard content is considered to have changed. |
| 97 NSString* kPasteboardEntryMD5Key = @"PasteboardEntryMD5"; | 85 NSString* kPasteboardEntryMD5Key = @"PasteboardEntryMD5"; |
| 98 // Key used to store the date of the latest pasteboard entry displayed in the | 86 base::TimeDelta kMaximumAgeOfClipboard = |
| 99 // omnibox. This is used to report metrics on pasteboard change. | 87 base::TimeDelta::FromSeconds(10); // Hours(3); |
|
Olivier
2016/08/10 13:46:41
don't forget to restore.
jif-google
2016/08/11 13:39:55
Acknowledged.
| |
| 100 NSString* kLastDisplayedPasteboardEntryKey = @"LastDisplayedPasteboardEntry"; | |
| 101 base::TimeDelta kMaximumAgeOfClipboard = base::TimeDelta::FromHours(3); | |
| 102 // Schemes accepted by the ClipboardRecentContentIOS. | 88 // Schemes accepted by the ClipboardRecentContentIOS. |
| 103 const char* kAuthorizedSchemes[] = { | 89 const char* kAuthorizedSchemes[] = { |
| 104 url::kHttpScheme, | 90 url::kHttpScheme, |
| 105 url::kHttpsScheme, | 91 url::kHttpsScheme, |
| 106 url::kDataScheme, | 92 url::kDataScheme, |
| 107 url::kAboutScheme, | 93 url::kAboutScheme, |
| 108 }; | 94 }; |
| 109 | 95 |
| 110 // Compute a hash consisting of the first 4 bytes of the MD5 hash of |string|. | 96 // Compute a hash consisting of the first 4 bytes of the MD5 hash of |string|. |
| 111 // This value is used to detect pasteboard content change. Keeping only 4 bytes | 97 // This value is used to detect pasteboard content change. Keeping only 4 bytes |
| 112 // is a privacy requirement to introduce collision and allow deniability of | 98 // is a privacy requirement to introduce collision and allow deniability of |
| 113 // having copied a given string. | 99 // having copied a given string. |
| 114 NSData* WeakMD5FromNSString(NSString* string) { | 100 NSData* WeakMD5FromNSString(NSString* string) { |
| 115 unsigned char hash[CC_MD5_DIGEST_LENGTH]; | 101 unsigned char hash[CC_MD5_DIGEST_LENGTH]; |
| 116 const char* c_string = [string UTF8String]; | 102 const char* c_string = [string UTF8String]; |
| 117 CC_MD5(c_string, strlen(c_string), hash); | 103 CC_MD5(c_string, strlen(c_string), hash); |
| 118 NSData* data = [NSData dataWithBytes:hash length:4]; | 104 NSData* data = [NSData dataWithBytes:hash length:4]; |
| 119 return data; | 105 return data; |
| 120 } | 106 } |
| 121 | 107 |
| 122 } // namespace | 108 } // namespace |
| 123 | 109 |
| 124 bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) const { | 110 bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) { |
| 125 DCHECK(url); | 111 DCHECK(url); |
| 112 CheckIfPasteboardChanged(); | |
| 126 if (GetClipboardContentAge() > kMaximumAgeOfClipboard) { | 113 if (GetClipboardContentAge() > kMaximumAgeOfClipboard) { |
| 127 return false; | 114 return false; |
| 128 } | 115 } |
| 129 | 116 |
| 130 if (url_from_pasteboard_cache_.is_valid()) { | 117 GURL url_from_pasteboard = URLFromPasteboard(); |
| 131 *url = url_from_pasteboard_cache_; | 118 if (url_from_pasteboard.is_valid()) { |
| 119 *url = url_from_pasteboard; | |
| 132 return true; | 120 return true; |
| 133 } | 121 } |
| 134 return false; | 122 return false; |
| 135 } | 123 } |
| 136 | 124 |
| 137 base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const { | 125 base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const { |
| 138 return base::TimeDelta::FromSeconds(static_cast<int64_t>( | 126 return base::TimeDelta::FromSeconds(static_cast<int64_t>( |
| 139 -[last_pasteboard_change_date_ timeIntervalSinceNow])); | 127 -[last_pasteboard_change_date_ timeIntervalSinceNow])); |
| 140 } | 128 } |
| 141 | 129 |
| 142 void ClipboardRecentContentIOS::SuppressClipboardContent() { | 130 void ClipboardRecentContentIOS::SuppressClipboardContent() { |
| 143 // User cleared the user data. The pasteboard entry must be removed from the | 131 // User cleared the user data. The pasteboard entry must be removed from the |
| 144 // omnibox list. Force entry expiration by setting copy date to 1970. | 132 // omnibox list. Force entry expiration by setting copy date to 1970. |
| 145 last_pasteboard_change_date_.reset( | 133 last_pasteboard_change_date_.reset( |
| 146 [[NSDate alloc] initWithTimeIntervalSince1970:0]); | 134 [[NSDate alloc] initWithTimeIntervalSince1970:0]); |
| 147 SaveToUserDefaults(); | 135 SaveToUserDefaults(); |
| 148 } | 136 } |
| 149 | 137 |
| 150 void ClipboardRecentContentIOS::PasteboardChanged() { | 138 void ClipboardRecentContentIOS::CheckIfPasteboardChanged() { |
| 151 url_from_pasteboard_cache_ = URLFromPasteboard(); | 139 if (!HasPasteboardChanged()) |
| 152 if (!url_from_pasteboard_cache_.is_empty()) { | 140 return; |
| 141 | |
| 142 GURL url_from_pasteboard = URLFromPasteboard(); | |
| 143 if (!url_from_pasteboard.is_valid()) { | |
|
Olivier
2016/08/10 13:46:41
remove !
jif-google
2016/08/11 13:39:55
Oh yes.
| |
| 153 base::RecordAction( | 144 base::RecordAction( |
| 154 base::UserMetricsAction("MobileOmniboxClipboardChanged")); | 145 base::UserMetricsAction("MobileOmniboxClipboardChanged")); |
| 155 } | 146 } |
| 156 last_pasteboard_change_date_.reset([[NSDate date] retain]); | 147 last_pasteboard_change_date_.reset([[NSDate date] retain]); |
| 157 last_pasteboard_change_count_ = [UIPasteboard generalPasteboard].changeCount; | 148 last_pasteboard_change_count_ = [UIPasteboard generalPasteboard].changeCount; |
| 158 NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; | 149 NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; |
| 159 if (!pasteboard_string) { | 150 if (!pasteboard_string) { |
| 160 pasteboard_string = @""; | 151 pasteboard_string = @""; |
| 161 } | 152 } |
| 162 NSData* MD5 = WeakMD5FromNSString(pasteboard_string); | 153 NSData* MD5 = WeakMD5FromNSString(pasteboard_string); |
| 163 last_pasteboard_entry_md5_.reset([MD5 retain]); | 154 last_pasteboard_entry_md5_.reset([MD5 retain]); |
| 164 SaveToUserDefaults(); | 155 SaveToUserDefaults(); |
| 165 } | 156 } |
| 166 | 157 |
| 167 ClipboardRecentContentIOS::ClipboardRecentContentIOS( | 158 ClipboardRecentContentIOS::ClipboardRecentContentIOS( |
| 168 const std::string& application_scheme, | 159 const std::string& application_scheme, |
| 169 NSUserDefaults* group_user_defaults) | 160 NSUserDefaults* group_user_defaults) |
| 170 : application_scheme_(application_scheme), | 161 : application_scheme_(application_scheme), |
| 171 shared_user_defaults_([group_user_defaults retain]) { | 162 shared_user_defaults_([group_user_defaults retain]) { |
| 172 Init(base::SysInfo::Uptime()); | 163 last_pasteboard_change_count_ = NSIntegerMax; |
| 164 LoadFromUserDefaults(); | |
| 165 | |
| 166 CheckIfPasteboardChanged(); | |
| 167 | |
| 168 // Makes sure |last_pasteboard_change_count_| was properly initialized. | |
| 169 DCHECK_NE(last_pasteboard_change_count_, NSIntegerMax); | |
| 170 notification_bridge_.reset( | |
| 171 [[ApplicationDidBecomeActiveNotificationListenerBridge alloc] | |
| 172 initWithDelegate:this]); | |
| 173 } | 173 } |
| 174 | 174 |
| 175 ClipboardRecentContentIOS::ClipboardRecentContentIOS( | 175 bool ClipboardRecentContentIOS::HasPasteboardChanged() const { |
| 176 const std::string& application_scheme, | |
| 177 base::TimeDelta uptime) | |
| 178 : application_scheme_(application_scheme), | |
| 179 shared_user_defaults_([[NSUserDefaults standardUserDefaults] retain]) { | |
| 180 Init(uptime); | |
| 181 } | |
| 182 | |
| 183 bool ClipboardRecentContentIOS::HasPasteboardChanged(base::TimeDelta uptime) { | |
| 184 // If |MD5Changed|, we know for sure there has been at least one pasteboard | 176 // If |MD5Changed|, we know for sure there has been at least one pasteboard |
| 185 // copy since last time it was checked. | 177 // copy since last time it was checked. |
| 186 // If the pasteboard content is still the same but the device was not | 178 // If the pasteboard content is still the same but the device was not |
| 187 // rebooted, the change count can be checked to see if it changed. | 179 // rebooted, the change count can be checked to see if it changed. |
| 188 // Note: due to a mismatch between the actual behavior and documentation, and | 180 // Note: due to a mismatch between the actual behavior and documentation, and |
| 189 // lack of consistency on different reboot scenarios, the change count cannot | 181 // lack of consistency on different reboot scenarios, the change count cannot |
| 190 // be checked after a reboot. | 182 // be checked after a reboot. |
| 191 // See radar://21833556 for more information. | 183 // See radar://21833556 for more information. |
| 192 NSInteger change_count = [UIPasteboard generalPasteboard].changeCount; | 184 NSInteger change_count = [UIPasteboard generalPasteboard].changeCount; |
| 193 bool change_count_changed = change_count != last_pasteboard_change_count_; | 185 bool change_count_changed = change_count != last_pasteboard_change_count_; |
| 194 | 186 |
| 195 bool not_rebooted = uptime > GetClipboardContentAge(); | 187 bool not_rebooted = Uptime() > GetClipboardContentAge(); |
| 196 if (not_rebooted) | 188 if (not_rebooted) |
| 197 return change_count_changed; | 189 return change_count_changed; |
| 198 | 190 |
| 199 NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; | 191 NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; |
| 200 if (!pasteboard_string) { | 192 if (!pasteboard_string) { |
| 201 pasteboard_string = @""; | 193 pasteboard_string = @""; |
| 202 } | 194 } |
| 203 NSData* md5 = WeakMD5FromNSString(pasteboard_string); | 195 NSData* md5 = WeakMD5FromNSString(pasteboard_string); |
| 204 BOOL md5_changed = ![md5 isEqualToData:last_pasteboard_entry_md5_]; | 196 BOOL md5_changed = ![md5 isEqualToData:last_pasteboard_entry_md5_]; |
| 205 | 197 |
| 206 return md5_changed; | 198 return md5_changed; |
| 207 } | 199 } |
| 208 | 200 |
| 209 bool ClipboardRecentContentIOS::GetCurrentURLFromClipboard(GURL* url) { | |
| 210 if (HasPasteboardChanged(base::SysInfo::Uptime())) { | |
| 211 PasteboardChanged(); | |
| 212 } | |
| 213 return GetRecentURLFromClipboard(url); | |
| 214 } | |
| 215 | |
| 216 void ClipboardRecentContentIOS::Init(base::TimeDelta uptime) { | |
| 217 last_pasteboard_change_count_ = NSIntegerMax; | |
| 218 url_from_pasteboard_cache_ = URLFromPasteboard(); | |
| 219 LoadFromUserDefaults(); | |
| 220 | |
| 221 if (HasPasteboardChanged(uptime)) | |
| 222 PasteboardChanged(); | |
| 223 | |
| 224 // Makes sure |last_pasteboard_change_count_| was properly initialized. | |
| 225 DCHECK_NE(last_pasteboard_change_count_, NSIntegerMax); | |
| 226 notification_bridge_.reset( | |
| 227 [[PasteboardNotificationListenerBridge alloc] initWithDelegate:this]); | |
| 228 } | |
| 229 | |
| 230 ClipboardRecentContentIOS::~ClipboardRecentContentIOS() { | 201 ClipboardRecentContentIOS::~ClipboardRecentContentIOS() { |
| 231 [notification_bridge_ disconnect]; | 202 [notification_bridge_ disconnect]; |
| 232 } | 203 } |
| 233 | 204 |
| 234 GURL ClipboardRecentContentIOS::URLFromPasteboard() { | 205 GURL ClipboardRecentContentIOS::URLFromPasteboard() { |
| 235 NSString* clipboard_string = [[UIPasteboard generalPasteboard] string]; | 206 NSString* clipboard_string = [[UIPasteboard generalPasteboard] string]; |
| 236 if (!clipboard_string) { | 207 if (!clipboard_string) { |
| 237 return GURL::EmptyGURL(); | 208 return GURL::EmptyGURL(); |
| 238 } | 209 } |
| 239 const std::string clipboard = base::SysNSStringToUTF8(clipboard_string); | 210 const std::string clipboard = base::SysNSStringToUTF8(clipboard_string); |
| 240 GURL gurl = GURL(clipboard); | 211 GURL gurl = GURL(clipboard); |
| 241 if (gurl.is_valid()) { | 212 if (gurl.is_valid()) { |
| 242 for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) { | 213 for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) { |
| 243 if (gurl.SchemeIs(kAuthorizedSchemes[i])) { | 214 if (gurl.SchemeIs(kAuthorizedSchemes[i])) { |
| 244 return gurl; | 215 return gurl; |
| 245 } | 216 } |
| 246 } | 217 } |
| 247 if (!application_scheme_.empty() && | 218 if (!application_scheme_.empty() && |
| 248 gurl.SchemeIs(application_scheme_.c_str())) { | 219 gurl.SchemeIs(application_scheme_.c_str())) { |
| 249 return gurl; | 220 return gurl; |
| 250 } | 221 } |
| 251 } | 222 } |
| 252 return GURL::EmptyGURL(); | 223 return GURL::EmptyGURL(); |
| 253 } | 224 } |
| 254 | 225 |
| 255 void ClipboardRecentContentIOS::RecentURLDisplayed() { | |
| 256 if ([last_pasteboard_change_date_ | |
| 257 isEqualToDate:last_displayed_pasteboard_entry_.get()]) { | |
| 258 return; | |
| 259 } | |
| 260 base::RecordAction(base::UserMetricsAction("MobileOmniboxClipboardChanged")); | |
| 261 last_pasteboard_change_date_ = last_displayed_pasteboard_entry_; | |
| 262 SaveToUserDefaults(); | |
| 263 } | |
| 264 | |
| 265 void ClipboardRecentContentIOS::LoadFromUserDefaults() { | 226 void ClipboardRecentContentIOS::LoadFromUserDefaults() { |
| 266 last_pasteboard_change_count_ = | 227 last_pasteboard_change_count_ = |
| 267 [shared_user_defaults_ integerForKey:kPasteboardChangeCountKey]; | 228 [shared_user_defaults_ integerForKey:kPasteboardChangeCountKey]; |
| 268 last_pasteboard_change_date_.reset( | 229 last_pasteboard_change_date_.reset( |
| 269 [[shared_user_defaults_ objectForKey:kPasteboardChangeDateKey] retain]); | 230 [[shared_user_defaults_ objectForKey:kPasteboardChangeDateKey] retain]); |
| 270 last_pasteboard_entry_md5_.reset( | 231 last_pasteboard_entry_md5_.reset( |
| 271 [[shared_user_defaults_ objectForKey:kPasteboardEntryMD5Key] retain]); | 232 [[shared_user_defaults_ objectForKey:kPasteboardEntryMD5Key] retain]); |
| 272 last_displayed_pasteboard_entry_.reset([[shared_user_defaults_ | |
| 273 objectForKey:kLastDisplayedPasteboardEntryKey] retain]); | |
| 274 | 233 |
| 275 DCHECK(!last_pasteboard_change_date_ || | 234 DCHECK(!last_pasteboard_change_date_ || |
| 276 [last_pasteboard_change_date_ isKindOfClass:[NSDate class]]); | 235 [last_pasteboard_change_date_ isKindOfClass:[NSDate class]]); |
| 277 } | 236 } |
| 278 | 237 |
| 279 void ClipboardRecentContentIOS::SaveToUserDefaults() { | 238 void ClipboardRecentContentIOS::SaveToUserDefaults() { |
| 280 [shared_user_defaults_ setInteger:last_pasteboard_change_count_ | 239 [shared_user_defaults_ setInteger:last_pasteboard_change_count_ |
| 281 forKey:kPasteboardChangeCountKey]; | 240 forKey:kPasteboardChangeCountKey]; |
| 282 [shared_user_defaults_ setObject:last_pasteboard_change_date_ | 241 [shared_user_defaults_ setObject:last_pasteboard_change_date_ |
| 283 forKey:kPasteboardChangeDateKey]; | 242 forKey:kPasteboardChangeDateKey]; |
| 284 [shared_user_defaults_ setObject:last_pasteboard_entry_md5_ | 243 [shared_user_defaults_ setObject:last_pasteboard_entry_md5_ |
| 285 forKey:kPasteboardEntryMD5Key]; | 244 forKey:kPasteboardEntryMD5Key]; |
| 286 [shared_user_defaults_ setObject:last_displayed_pasteboard_entry_ | |
| 287 forKey:kLastDisplayedPasteboardEntryKey]; | |
| 288 } | 245 } |
| 246 | |
| 247 base::TimeDelta ClipboardRecentContentIOS::Uptime() const { | |
| 248 return base::SysInfo::Uptime(); | |
| 249 } | |
| OLD | NEW |