Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1531)

Side by Side Diff: components/open_from_clipboard/clipboard_recent_content_impl_ios.mm

Issue 2782823003: Rewrite implementation of ClipboardRecentContent in Objective C. (Closed)
Patch Set: build.gn fix Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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* const kPasteboardChangeCountKey = @"PasteboardChangeCount";
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* const 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* const kPasteboardEntryMD5Key = @"PasteboardEntryMD5";
29 // Maximum age of clipboard in seconds.
30 NSTimeInterval const kMaximumAgeOfClipboard = 3 * 60 * 60;
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<NSString*>*)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 clipboardContentAge] >= [self uptime];
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*)recentURLFromClipboard {
152 [self updateIfNeeded];
153 if ([self clipboardContentAge] > kMaximumAgeOfClipboard) {
154 return nil;
155 }
156 return [self URLFromPasteboard];
157 }
158
159 - (NSTimeInterval)clipboardContentAge {
160 return -[self.lastPasteboardChangeDate timeIntervalSinceNow];
161 }
162
163 - (void)suppressClipboardContent {
164 // User cleared the user data. The pasteboard entry must be removed from the
165 // omnibox list. Force entry expiration by setting copy date to 1970.
166 self.lastPasteboardChangeDate =
167 [[NSDate alloc] initWithTimeIntervalSince1970:0];
168 [self saveToUserDefaults];
169 }
170
171 - (void)updateIfNeeded {
172 if (![self hasPasteboardChanged]) {
173 return;
174 }
175
176 [self.delegate onClipboardChanged];
177
178 self.lastPasteboardChangeDate = [NSDate date];
179 self.lastPasteboardChangeCount = [UIPasteboard generalPasteboard].changeCount;
180 self.lastPasteboardEntryMD5 = [self getCurrentMD5];
181
182 [self saveToUserDefaults];
183 }
184
185 - (NSURL*)URLFromPasteboard {
186 NSString* clipboardString = [UIPasteboard generalPasteboard].string;
187
188 NSURL* url = [NSURL URLWithString:clipboardString];
189 if (![self.authorizedSchemes containsObject:url.scheme]) {
190 return nil;
191 }
192 return url;
193 }
194
195 - (void)loadFromUserDefaults {
196 self.lastPasteboardChangeCount =
197 [self.sharedUserDefaults integerForKey:kPasteboardChangeCountKey];
198 self.lastPasteboardChangeDate = base::mac::ObjCCastStrict<NSDate>(
199 [self.sharedUserDefaults objectForKey:kPasteboardChangeDateKey]);
200 self.lastPasteboardEntryMD5 = base::mac::ObjCCastStrict<NSData>(
201 [self.sharedUserDefaults objectForKey:kPasteboardEntryMD5Key]);
202 }
203
204 - (void)saveToUserDefaults {
205 [self.sharedUserDefaults setInteger:self.lastPasteboardChangeCount
206 forKey:kPasteboardChangeCountKey];
207 [self.sharedUserDefaults setObject:self.lastPasteboardChangeDate
208 forKey:kPasteboardChangeDateKey];
209 [self.sharedUserDefaults setObject:self.lastPasteboardEntryMD5
210 forKey:kPasteboardEntryMD5Key];
211 }
212
213 - (NSTimeInterval)uptime {
214 return base::SysInfo::Uptime().InSecondsF();
215 }
216
217 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698