Chromium Code Reviews| Index: components/open_from_clipboard/clipboard_recent_content_ios.mm |
| diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.mm b/components/open_from_clipboard/clipboard_recent_content_ios.mm |
| index 0ef8e97124dfac6e7a593f981902839c67878ac0..394f46833ea1df534e1e6e451f52c3ff2071f884 100644 |
| --- a/components/open_from_clipboard/clipboard_recent_content_ios.mm |
| +++ b/components/open_from_clipboard/clipboard_recent_content_ios.mm |
| @@ -15,233 +15,83 @@ |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/sys_info.h" |
| +#import "components/open_from_clipboard/clipboard_recent_content_ios_impl.h" |
| +#import "net/base/mac/url_conversions.h" |
| #include "url/gurl.h" |
| #include "url/url_constants.h" |
| -// Bridge that forwards UIApplicationDidBecomeActiveNotification notifications |
| -// to its delegate. |
| -@interface ApplicationDidBecomeActiveNotificationListenerBridge : NSObject |
| - |
| -// Initialize the ApplicationDidBecomeActiveNotificationListenerBridge with |
| -// |delegate| which must not be null. |
| -- (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate |
| - NS_DESIGNATED_INITIALIZER; |
| - |
| -- (instancetype)init NS_UNAVAILABLE; |
| - |
| -@end |
| - |
| -@implementation ApplicationDidBecomeActiveNotificationListenerBridge { |
| - ClipboardRecentContentIOS* _delegate; |
| -} |
| - |
| -- (instancetype)init { |
| - NOTREACHED(); |
| - return nil; |
| -} |
| - |
| -- (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate { |
| - DCHECK(delegate); |
| - self = [super init]; |
| - if (self) { |
| - _delegate = delegate; |
| - [[NSNotificationCenter defaultCenter] |
| - addObserver:self |
| - selector:@selector(didBecomeActive:) |
| - name:UIApplicationDidBecomeActiveNotification |
| - object:nil]; |
| - } |
| - return self; |
| +namespace { |
| +// Schemes accepted by the ClipboardRecentContentIOS. |
| +const char* kAuthorizedSchemes[] = { |
| + url::kHttpScheme, url::kHttpsScheme, url::kDataScheme, url::kAboutScheme, |
| +}; |
| } |
| -- (void)dealloc { |
| - [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| - [super dealloc]; |
| -} |
| +@interface ClipboardRecentContentMetricsDelegateImpl |
| + : NSObject<ClipboardRecentContentMetricsDelegate> |
| +@end |
| -- (void)didBecomeActive:(NSNotification*)notification { |
| - if (_delegate) { |
| - _delegate->LoadFromUserDefaults(); |
| - _delegate->UpdateIfNeeded(); |
| - } |
| -} |
| +@implementation ClipboardRecentContentMetricsDelegateImpl |
| -- (void)disconnect { |
| - _delegate = nullptr; |
| +- (void)onClipboardChanged { |
| + base::RecordAction(base::UserMetricsAction("MobileOmniboxClipboardChanged")); |
|
sdefresne
2017/04/03 09:27:16
(outside of the CL) I think action that is recorde
|
| } |
| @end |
| -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* 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* 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* kPasteboardEntryMD5Key = @"PasteboardEntryMD5"; |
| -base::TimeDelta kMaximumAgeOfClipboard = base::TimeDelta::FromHours(3); |
| -// Schemes accepted by the ClipboardRecentContentIOS. |
| -const char* kAuthorizedSchemes[] = { |
| - url::kHttpScheme, |
| - url::kHttpsScheme, |
| - url::kDataScheme, |
| - url::kAboutScheme, |
| -}; |
| +ClipboardRecentContentIOS::ClipboardRecentContentIOS( |
| + const std::string& application_scheme, |
| + NSUserDefaults* group_user_defaults) { |
| + ClipboardRecentContentMetricsDelegateImpl* metricsDelegate = |
|
sdefresne
2017/04/03 09:27:16
s/metricsDelegate/metrics_delegate/.
lody
2017/04/04 13:42:18
Done.
|
| + [[ClipboardRecentContentMetricsDelegateImpl alloc] init]; |
| -// 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; |
| + implementation_.reset([[ClipboardRecentContentIOSImpl alloc] |
| + initWithDelegate:metricsDelegate |
| + authorizedSchemes:GetAuthorizedSchemeList(application_scheme) |
| + userDefaults:group_user_defaults]); |
| } |
| -} // namespace |
| - |
| bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) { |
| DCHECK(url); |
| - UpdateIfNeeded(); |
| - if (GetClipboardContentAge() > kMaximumAgeOfClipboard) { |
| - return false; |
| - } |
| - |
| - GURL url_from_pasteboard = URLFromPasteboard(); |
| - if (url_from_pasteboard.is_valid()) { |
| - *url = url_from_pasteboard; |
| - return true; |
| + NSURL* nsurl = [implementation_ getRecentURLFromClipboard]; |
|
sdefresne
2017/04/03 09:27:16
url_from_pasteboard
lody
2017/04/04 13:42:18
Done.
|
| + if (nsurl != nil) { |
|
sdefresne
2017/04/03 09:27:16
No need to test nsurl for nil, net::GURLWithNSURL(
lody
2017/04/04 13:42:18
Done.
|
| + GURL tempUrl = net::GURLWithNSURL(nsurl); |
|
sdefresne
2017/04/03 09:27:16
NSURL* url_from_pasteboard = [implementation_ getR
lody
2017/04/04 13:42:18
Done.
|
| + if (tempUrl.is_valid()) { |
| + *url = std::move(tempUrl); |
| + return true; |
| + } |
| } |
| return false; |
| } |
| +ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {} |
| + |
| base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const { |
| - return base::TimeDelta::FromSeconds(static_cast<int64_t>( |
| - -[last_pasteboard_change_date_ timeIntervalSinceNow])); |
| + return base::TimeDelta::FromSeconds( |
| + static_cast<int64_t>([implementation_ getClipboardContentAge])); |
| } |
| void ClipboardRecentContentIOS::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. |
| - last_pasteboard_change_date_.reset( |
| - [[NSDate alloc] initWithTimeIntervalSince1970:0]); |
| - SaveToUserDefaults(); |
| -} |
| - |
| -void ClipboardRecentContentIOS::UpdateIfNeeded() { |
| - if (!HasPasteboardChanged()) |
| - return; |
| - |
| - base::RecordAction(base::UserMetricsAction("MobileOmniboxClipboardChanged")); |
| - |
| - GURL url_from_pasteboard = URLFromPasteboard(); |
| - last_pasteboard_change_date_.reset([[NSDate date] retain]); |
| - last_pasteboard_change_count_ = [UIPasteboard generalPasteboard].changeCount; |
| - NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; |
| - if (!pasteboard_string) { |
| - pasteboard_string = @""; |
| - } |
| - NSData* MD5 = WeakMD5FromNSString(pasteboard_string); |
| - last_pasteboard_entry_md5_.reset([MD5 retain]); |
| - SaveToUserDefaults(); |
| -} |
| - |
| -ClipboardRecentContentIOS::ClipboardRecentContentIOS( |
| - const std::string& application_scheme, |
| - NSUserDefaults* group_user_defaults) |
| - : application_scheme_(application_scheme), |
| - shared_user_defaults_([group_user_defaults retain]) { |
| - last_pasteboard_change_count_ = NSIntegerMax; |
| - LoadFromUserDefaults(); |
| - |
| - UpdateIfNeeded(); |
| - |
| - // Makes sure |last_pasteboard_change_count_| was properly initialized. |
| - DCHECK_NE(last_pasteboard_change_count_, NSIntegerMax); |
| - notification_bridge_.reset( |
| - [[ApplicationDidBecomeActiveNotificationListenerBridge alloc] |
| - initWithDelegate:this]); |
| + [implementation_ suppressClipboardContent]; |
| } |
| -bool ClipboardRecentContentIOS::HasPasteboardChanged() const { |
| - // 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. |
| - NSInteger change_count = [UIPasteboard generalPasteboard].changeCount; |
| - bool change_count_changed = change_count != last_pasteboard_change_count_; |
| - |
| - bool not_rebooted = Uptime() > GetClipboardContentAge(); |
| - if (not_rebooted) |
| - return change_count_changed; |
| - |
| - NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; |
| - if (!pasteboard_string) { |
| - pasteboard_string = @""; |
| - } |
| - NSData* md5 = WeakMD5FromNSString(pasteboard_string); |
| - BOOL md5_changed = ![md5 isEqualToData:last_pasteboard_entry_md5_]; |
| - |
| - return md5_changed; |
| +void ClipboardRecentContentIOS::SaveToUserDefaults() { |
| + [implementation_ saveToUserDefaults]; |
| } |
| -ClipboardRecentContentIOS::~ClipboardRecentContentIOS() { |
| - [notification_bridge_ disconnect]; |
| +void ClipboardRecentContentIOS::SetLastPasteboardChangeDate(NSDate* date) { |
| + [implementation_ setLastPasteboardChangeDate:date]; |
| } |
| -GURL ClipboardRecentContentIOS::URLFromPasteboard() { |
| - NSString* clipboard_string = [[UIPasteboard generalPasteboard] string]; |
| - if (!clipboard_string) { |
| - return GURL::EmptyGURL(); |
| +NSArray* ClipboardRecentContentIOS::GetAuthorizedSchemeList( |
|
sdefresne
2017/04/03 09:27:16
This can be a free function, no need to export thi
lody
2017/04/04 13:42:18
Done.
|
| + const std::string& application_scheme) { |
| + NSMutableArray* schemes = [NSMutableArray array]; |
| + for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) { |
| + [schemes addObject:base::SysUTF8ToNSString(kAuthorizedSchemes[i])]; |
| } |
| - const std::string clipboard = base::SysNSStringToUTF8(clipboard_string); |
| - GURL gurl = GURL(clipboard); |
| - if (gurl.is_valid()) { |
| - for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) { |
| - if (gurl.SchemeIs(kAuthorizedSchemes[i])) { |
| - return gurl; |
| - } |
| - } |
| - if (!application_scheme_.empty() && |
| - gurl.SchemeIs(application_scheme_.c_str())) { |
| - return gurl; |
| - } |
| + if (!application_scheme.empty()) { |
| + [schemes addObject:base::SysUTF8ToNSString(application_scheme)]; |
| } |
| - return GURL::EmptyGURL(); |
| -} |
| - |
| -void ClipboardRecentContentIOS::LoadFromUserDefaults() { |
| - last_pasteboard_change_count_ = |
| - [shared_user_defaults_ integerForKey:kPasteboardChangeCountKey]; |
| - last_pasteboard_change_date_.reset( |
| - [[shared_user_defaults_ objectForKey:kPasteboardChangeDateKey] retain]); |
| - last_pasteboard_entry_md5_.reset( |
| - [[shared_user_defaults_ objectForKey:kPasteboardEntryMD5Key] retain]); |
| - |
| - DCHECK(!last_pasteboard_change_date_ || |
| - [last_pasteboard_change_date_ isKindOfClass:[NSDate class]]); |
| -} |
| - |
| -void ClipboardRecentContentIOS::SaveToUserDefaults() { |
| - [shared_user_defaults_ setInteger:last_pasteboard_change_count_ |
| - forKey:kPasteboardChangeCountKey]; |
| - [shared_user_defaults_ setObject:last_pasteboard_change_date_ |
| - forKey:kPasteboardChangeDateKey]; |
| - [shared_user_defaults_ setObject:last_pasteboard_entry_md5_ |
| - forKey:kPasteboardEntryMD5Key]; |
| -} |
| -base::TimeDelta ClipboardRecentContentIOS::Uptime() const { |
| - return base::SysInfo::Uptime(); |
| + return schemes; |
| } |