| 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
|
|
|