OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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. | |
4 | |
5 #import "components/open_from_clipboard/clipboard_recent_content_impl_ios.h" | |
6 | |
7 #import <CommonCrypto/CommonDigest.h> | |
8 #import <UIKit/UIKit.h> | |
9 | |
10 #import "base/mac/foundation_util.h" | |
11 #include "base/strings/sys_string_conversions.h" | |
12 #include "base/sys_info.h" | |
13 | |
14 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
15 #error "This file requires ARC support." | |
16 #endif | |
17 | |
18 namespace { | |
19 // Key used to store the pasteboard's current change count. If when resuming | |
20 // chrome the pasteboard's change count is different from the stored one, then | |
21 // it means that the pasteboard's content has changed. | |
22 NSString* kPasteboardChangeCountKey = @"PasteboardChangeCount"; | |
marq (ping after 24h)
2017/04/05 07:58:10
NSString* const kPasteboardChangeCountKey ..., her
lody
2017/04/06 11:27:22
Done.
| |
23 // Key used to store the last date at which it was detected that the pasteboard | |
24 // changed. It is used to evaluate the age of the pasteboard's content. | |
25 NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate"; | |
26 // Key used to store the hash of the content of the pasteboard. Whenever the | |
27 // hash changed, the pasteboard content is considered to have changed. | |
28 NSString* kPasteboardEntryMD5Key = @"PasteboardEntryMD5"; | |
29 // Maximum age of clipboard in seconds. | |
30 NSTimeInterval kMaximumAgeOfClipboard = 3 * 60 * 60; | |
marq (ping after 24h)
2017/04/05 07:58:10
const?
lody
2017/04/06 11:27:22
Done.
| |
31 | |
32 // Compute a hash consisting of the first 4 bytes of the MD5 hash of |string|. | |
33 // This value is used to detect pasteboard content change. Keeping only 4 bytes | |
34 // is a privacy requirement to introduce collision and allow deniability of | |
35 // having copied a given string. | |
36 NSData* WeakMD5FromNSString(NSString* string) { | |
37 unsigned char hash[CC_MD5_DIGEST_LENGTH]; | |
38 const std::string clipboard = base::SysNSStringToUTF8(string); | |
39 const char* c_string = clipboard.c_str(); | |
40 CC_MD5(c_string, strlen(c_string), hash); | |
41 NSData* data = [NSData dataWithBytes:hash length:4]; | |
42 return data; | |
43 } | |
44 | |
45 } // namespace | |
46 | |
47 @interface ClipboardRecentContentImplIOS () | |
48 | |
49 // The user defaults from the app group used to optimize the pasteboard change | |
50 // detection. | |
51 @property(nonatomic, strong) NSUserDefaults* sharedUserDefaults; | |
52 // The pasteboard's change count. Increases everytime the pasteboard changes. | |
53 @property(nonatomic) NSInteger lastPasteboardChangeCount; | |
54 // MD5 hash of the last registered pasteboard entry. | |
55 @property(nonatomic, strong) NSData* lastPasteboardEntryMD5; | |
56 // Contains the authorized schemes for URLs. | |
57 @property(nonatomic, readonly) NSSet* authorizedSchemes; | |
58 // Delegate for metrics. | |
59 @property(nonatomic, strong) id<ClipboardRecentContentDelegate> delegate; | |
60 | |
61 // If the content of the pasteboard has changed, updates the change count, | |
62 // change date, and md5 of the latest pasteboard entry if necessary. | |
63 - (void)updateIfNeeded; | |
64 | |
65 // Returns whether the pasteboard changed since the last time a pasteboard | |
66 // change was detected. | |
67 - (BOOL)hasPasteboardChanged; | |
68 | |
69 // Loads information from the user defaults about the latest pasteboard entry. | |
70 - (void)loadFromUserDefaults; | |
71 | |
72 // Returns the URL contained in the clipboard (if any). | |
73 - (NSURL*)URLFromPasteboard; | |
74 | |
75 // Returns the uptime. | |
76 - (NSTimeInterval)uptime; | |
77 | |
78 @end | |
79 | |
80 @implementation ClipboardRecentContentImplIOS | |
81 | |
82 @synthesize lastPasteboardChangeCount = _lastPasteboardChangeCount; | |
83 @synthesize lastPasteboardChangeDate = _lastPasteboardChangeDate; | |
84 @synthesize lastPasteboardEntryMD5 = _lastPasteboardEntryMD5; | |
85 @synthesize sharedUserDefaults = _sharedUserDefaults; | |
86 @synthesize authorizedSchemes = _authorizedSchemes; | |
87 @synthesize delegate = _delegate; | |
88 | |
89 - (instancetype)initWithAuthorizedSchemes:(NSSet*)authorizedSchemes | |
90 userDefaults:(NSUserDefaults*)groupUserDefaults | |
91 delegate:(id<ClipboardRecentContentDelegate>) | |
92 delegate { | |
93 self = [super init]; | |
94 if (self) { | |
95 _delegate = delegate; | |
96 _authorizedSchemes = authorizedSchemes; | |
97 _sharedUserDefaults = groupUserDefaults; | |
98 | |
99 _lastPasteboardChangeCount = NSIntegerMax; | |
100 [self loadFromUserDefaults]; | |
101 [self updateIfNeeded]; | |
102 | |
103 // Makes sure |last_pasteboard_change_count_| was properly initialized. | |
104 DCHECK_NE(_lastPasteboardChangeCount, NSIntegerMax); | |
105 [[NSNotificationCenter defaultCenter] | |
106 addObserver:self | |
107 selector:@selector(didBecomeActive:) | |
108 name:UIApplicationDidBecomeActiveNotification | |
109 object:nil]; | |
110 } | |
111 return self; | |
112 } | |
113 | |
114 - (void)dealloc { | |
115 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
116 } | |
117 | |
118 - (void)didBecomeActive:(NSNotification*)notification { | |
119 [self loadFromUserDefaults]; | |
120 [self updateIfNeeded]; | |
121 } | |
122 | |
123 - (NSData*)getCurrentMD5 { | |
124 NSString* pasteboardString = [UIPasteboard generalPasteboard].string; | |
125 NSData* md5 = WeakMD5FromNSString(pasteboardString); | |
126 | |
127 return md5; | |
128 } | |
129 | |
130 - (BOOL)hasPasteboardChanged { | |
131 // If |MD5Changed|, we know for sure there has been at least one pasteboard | |
132 // copy since last time it was checked. | |
133 // If the pasteboard content is still the same but the device was not | |
134 // rebooted, the change count can be checked to see if it changed. | |
135 // Note: due to a mismatch between the actual behavior and documentation, and | |
136 // lack of consistency on different reboot scenarios, the change count cannot | |
137 // be checked after a reboot. | |
138 // See radar://21833556 for more information. | |
139 bool deviceRebooted = [self getClipboardContentAge] >= [self uptime]; | |
marq (ping after 24h)
2017/04/05 07:58:10
BOOL
lody
2017/04/06 11:27:22
Done.
| |
140 if (!deviceRebooted) { | |
141 NSInteger changeCount = [UIPasteboard generalPasteboard].changeCount; | |
142 bool changeCountChanged = changeCount != self.lastPasteboardChangeCount; | |
143 return changeCountChanged; | |
144 } | |
145 | |
146 BOOL md5Changed = | |
147 ![[self getCurrentMD5] isEqualToData:self.lastPasteboardEntryMD5]; | |
148 return md5Changed; | |
149 } | |
150 | |
151 - (NSURL*)getRecentURLFromClipboard { | |
152 [self updateIfNeeded]; | |
153 if ([self getClipboardContentAge] > kMaximumAgeOfClipboard) { | |
154 return nil; | |
155 } | |
156 | |
157 NSURL* urlFromPasteboard = [self URLFromPasteboard]; | |
158 if (urlFromPasteboard) { | |
marq (ping after 24h)
2017/04/05 07:58:10
If this conditional fails, that means |urlFromPast
lody
2017/04/06 11:27:22
Done.
| |
159 return urlFromPasteboard; | |
160 } | |
161 return nil; | |
162 } | |
163 | |
164 - (NSTimeInterval)getClipboardContentAge { | |
marq (ping after 24h)
2017/04/05 07:58:10
remove 'get'.
Method needs a comment somewhere.
lody
2017/04/06 11:27:22
Done
It's in the .h
| |
165 return -[self.lastPasteboardChangeDate timeIntervalSinceNow]; | |
166 } | |
167 | |
168 - (void)suppressClipboardContent { | |
169 // User cleared the user data. The pasteboard entry must be removed from the | |
170 // omnibox list. Force entry expiration by setting copy date to 1970. | |
171 self.lastPasteboardChangeDate = | |
172 [[NSDate alloc] initWithTimeIntervalSince1970:0]; | |
173 [self saveToUserDefaults]; | |
174 } | |
175 | |
176 - (void)updateIfNeeded { | |
177 if (![self hasPasteboardChanged]) { | |
178 return; | |
179 } | |
180 | |
181 [self.delegate onClipboardChanged]; | |
182 | |
183 self.lastPasteboardChangeDate = [NSDate date]; | |
184 self.lastPasteboardChangeCount = [UIPasteboard generalPasteboard].changeCount; | |
185 self.lastPasteboardEntryMD5 = [self getCurrentMD5]; | |
186 | |
187 [self saveToUserDefaults]; | |
188 } | |
189 | |
190 - (NSURL*)URLFromPasteboard { | |
191 NSString* clipboardString = [UIPasteboard generalPasteboard].string; | |
192 if (!clipboardString) { | |
marq (ping after 24h)
2017/04/05 07:58:10
You can remove a lot of the nil checking, since [N
lody
2017/04/06 11:27:22
Done.
| |
193 return nil; | |
194 } | |
195 | |
196 NSURL* url = [NSURL URLWithString:clipboardString]; | |
197 if (!url || ![self.authorizedSchemes containsObject:url.scheme]) { | |
198 return nil; | |
199 } | |
200 return url; | |
201 } | |
202 | |
203 - (void)loadFromUserDefaults { | |
204 self.lastPasteboardChangeCount = | |
205 [self.sharedUserDefaults integerForKey:kPasteboardChangeCountKey]; | |
206 self.lastPasteboardChangeDate = base::mac::ObjCCastStrict<NSDate>( | |
207 [self.sharedUserDefaults objectForKey:kPasteboardChangeDateKey]); | |
208 self.lastPasteboardEntryMD5 = | |
marq (ping after 24h)
2017/04/05 07:58:10
Why not also cast this value?
lody
2017/04/06 11:27:22
Done.
| |
209 [self.sharedUserDefaults objectForKey:kPasteboardEntryMD5Key]; | |
210 } | |
211 | |
212 - (void)saveToUserDefaults { | |
213 [self.sharedUserDefaults setInteger:self.lastPasteboardChangeCount | |
214 forKey:kPasteboardChangeCountKey]; | |
215 [self.sharedUserDefaults setObject:self.lastPasteboardChangeDate | |
216 forKey:kPasteboardChangeDateKey]; | |
217 [self.sharedUserDefaults setObject:self.lastPasteboardEntryMD5 | |
218 forKey:kPasteboardEntryMD5Key]; | |
219 } | |
220 | |
221 - (NSTimeInterval)uptime { | |
222 return base::SysInfo::Uptime().InSecondsF(); | |
223 } | |
224 | |
225 @end | |
OLD | NEW |