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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
diff --git a/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm b/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
new file mode 100644
index 0000000000000000000000000000000000000000..669965ced16c4fa9ff7facfdd14c52765a14e340
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
@@ -0,0 +1,217 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "components/open_from_clipboard/clipboard_recent_content_impl_ios.h"
+
+#import <CommonCrypto/CommonDigest.h>
+#import <UIKit/UIKit.h>
+
+#import "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/sys_info.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Key used to store the pasteboard's current change count. If when resuming
+// chrome the pasteboard's change count is different from the stored one, then
+// it means that the pasteboard's content has changed.
+NSString* const kPasteboardChangeCountKey = @"PasteboardChangeCount";
+// Key used to store the last date at which it was detected that the pasteboard
+// changed. It is used to evaluate the age of the pasteboard's content.
+NSString* const kPasteboardChangeDateKey = @"PasteboardChangeDate";
+// Key used to store the hash of the content of the pasteboard. Whenever the
+// hash changed, the pasteboard content is considered to have changed.
+NSString* const kPasteboardEntryMD5Key = @"PasteboardEntryMD5";
+// Maximum age of clipboard in seconds.
+NSTimeInterval const kMaximumAgeOfClipboard = 3 * 60 * 60;
+
+// Compute a hash consisting of the first 4 bytes of the MD5 hash of |string|.
+// This value is used to detect pasteboard content change. Keeping only 4 bytes
+// is a privacy requirement to introduce collision and allow deniability of
+// having copied a given string.
+NSData* WeakMD5FromNSString(NSString* string) {
+ unsigned char hash[CC_MD5_DIGEST_LENGTH];
+ const std::string clipboard = base::SysNSStringToUTF8(string);
+ const char* c_string = clipboard.c_str();
+ CC_MD5(c_string, strlen(c_string), hash);
+ NSData* data = [NSData dataWithBytes:hash length:4];
+ return data;
+}
+
+} // namespace
+
+@interface ClipboardRecentContentImplIOS ()
+
+// The user defaults from the app group used to optimize the pasteboard change
+// detection.
+@property(nonatomic, strong) NSUserDefaults* sharedUserDefaults;
+// The pasteboard's change count. Increases everytime the pasteboard changes.
+@property(nonatomic) NSInteger lastPasteboardChangeCount;
+// MD5 hash of the last registered pasteboard entry.
+@property(nonatomic, strong) NSData* lastPasteboardEntryMD5;
+// Contains the authorized schemes for URLs.
+@property(nonatomic, readonly) NSSet* authorizedSchemes;
+// Delegate for metrics.
+@property(nonatomic, strong) id<ClipboardRecentContentDelegate> delegate;
+
+// If the content of the pasteboard has changed, updates the change count,
+// change date, and md5 of the latest pasteboard entry if necessary.
+- (void)updateIfNeeded;
+
+// Returns whether the pasteboard changed since the last time a pasteboard
+// change was detected.
+- (BOOL)hasPasteboardChanged;
+
+// Loads information from the user defaults about the latest pasteboard entry.
+- (void)loadFromUserDefaults;
+
+// Returns the URL contained in the clipboard (if any).
+- (NSURL*)URLFromPasteboard;
+
+// Returns the uptime.
+- (NSTimeInterval)uptime;
+
+@end
+
+@implementation ClipboardRecentContentImplIOS
+
+@synthesize lastPasteboardChangeCount = _lastPasteboardChangeCount;
+@synthesize lastPasteboardChangeDate = _lastPasteboardChangeDate;
+@synthesize lastPasteboardEntryMD5 = _lastPasteboardEntryMD5;
+@synthesize sharedUserDefaults = _sharedUserDefaults;
+@synthesize authorizedSchemes = _authorizedSchemes;
+@synthesize delegate = _delegate;
+
+- (instancetype)initWithAuthorizedSchemes:(NSSet<NSString*>*)authorizedSchemes
+ userDefaults:(NSUserDefaults*)groupUserDefaults
+ delegate:(id<ClipboardRecentContentDelegate>)
+ delegate {
+ self = [super init];
+ if (self) {
+ _delegate = delegate;
+ _authorizedSchemes = authorizedSchemes;
+ _sharedUserDefaults = groupUserDefaults;
+
+ _lastPasteboardChangeCount = NSIntegerMax;
+ [self loadFromUserDefaults];
+ [self updateIfNeeded];
+
+ // Makes sure |last_pasteboard_change_count_| was properly initialized.
+ DCHECK_NE(_lastPasteboardChangeCount, NSIntegerMax);
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(didBecomeActive:)
+ name:UIApplicationDidBecomeActiveNotification
+ object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)didBecomeActive:(NSNotification*)notification {
+ [self loadFromUserDefaults];
+ [self updateIfNeeded];
+}
+
+- (NSData*)getCurrentMD5 {
+ NSString* pasteboardString = [UIPasteboard generalPasteboard].string;
+ NSData* md5 = WeakMD5FromNSString(pasteboardString);
+
+ return md5;
+}
+
+- (BOOL)hasPasteboardChanged {
+ // If |MD5Changed|, we know for sure there has been at least one pasteboard
+ // copy since last time it was checked.
+ // If the pasteboard content is still the same but the device was not
+ // rebooted, the change count can be checked to see if it changed.
+ // Note: due to a mismatch between the actual behavior and documentation, and
+ // lack of consistency on different reboot scenarios, the change count cannot
+ // be checked after a reboot.
+ // See radar://21833556 for more information.
+ BOOL deviceRebooted = [self clipboardContentAge] >= [self uptime];
+ if (!deviceRebooted) {
+ NSInteger changeCount = [UIPasteboard generalPasteboard].changeCount;
+ bool changeCountChanged = changeCount != self.lastPasteboardChangeCount;
+ return changeCountChanged;
+ }
+
+ BOOL md5Changed =
+ ![[self getCurrentMD5] isEqualToData:self.lastPasteboardEntryMD5];
+ return md5Changed;
+}
+
+- (NSURL*)recentURLFromClipboard {
+ [self updateIfNeeded];
+ if ([self clipboardContentAge] > kMaximumAgeOfClipboard) {
+ return nil;
+ }
+ return [self URLFromPasteboard];
+}
+
+- (NSTimeInterval)clipboardContentAge {
+ return -[self.lastPasteboardChangeDate timeIntervalSinceNow];
+}
+
+- (void)suppressClipboardContent {
+ // User cleared the user data. The pasteboard entry must be removed from the
+ // omnibox list. Force entry expiration by setting copy date to 1970.
+ self.lastPasteboardChangeDate =
+ [[NSDate alloc] initWithTimeIntervalSince1970:0];
+ [self saveToUserDefaults];
+}
+
+- (void)updateIfNeeded {
+ if (![self hasPasteboardChanged]) {
+ return;
+ }
+
+ [self.delegate onClipboardChanged];
+
+ self.lastPasteboardChangeDate = [NSDate date];
+ self.lastPasteboardChangeCount = [UIPasteboard generalPasteboard].changeCount;
+ self.lastPasteboardEntryMD5 = [self getCurrentMD5];
+
+ [self saveToUserDefaults];
+}
+
+- (NSURL*)URLFromPasteboard {
+ NSString* clipboardString = [UIPasteboard generalPasteboard].string;
+
+ NSURL* url = [NSURL URLWithString:clipboardString];
+ if (![self.authorizedSchemes containsObject:url.scheme]) {
+ return nil;
+ }
+ return url;
+}
+
+- (void)loadFromUserDefaults {
+ self.lastPasteboardChangeCount =
+ [self.sharedUserDefaults integerForKey:kPasteboardChangeCountKey];
+ self.lastPasteboardChangeDate = base::mac::ObjCCastStrict<NSDate>(
+ [self.sharedUserDefaults objectForKey:kPasteboardChangeDateKey]);
+ self.lastPasteboardEntryMD5 = base::mac::ObjCCastStrict<NSData>(
+ [self.sharedUserDefaults objectForKey:kPasteboardEntryMD5Key]);
+}
+
+- (void)saveToUserDefaults {
+ [self.sharedUserDefaults setInteger:self.lastPasteboardChangeCount
+ forKey:kPasteboardChangeCountKey];
+ [self.sharedUserDefaults setObject:self.lastPasteboardChangeDate
+ forKey:kPasteboardChangeDateKey];
+ [self.sharedUserDefaults setObject:self.lastPasteboardEntryMD5
+ forKey:kPasteboardEntryMD5Key];
+}
+
+- (NSTimeInterval)uptime {
+ return base::SysInfo::Uptime().InSecondsF();
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698