[Sync] Add auto-generated ModelType in Java.
[chromium-blink-merge.git] / components / open_from_clipboard / clipboard_recent_content_ios.mm
blobd27d2566eb2d3f4b2f893cdbe50c517cd46fbaef
1 // Copyright 2015 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.
5 #import "components/open_from_clipboard/clipboard_recent_content_ios.h"
7 #import <UIKit/UIKit.h>
9 #import "base/ios/ios_util.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/metrics/user_metrics.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/sys_info.h"
15 #include "url/gurl.h"
16 #include "url/url_constants.h"
18 // Bridge that forwards pasteboard change notifications to its delegate.
19 @interface PasteboardNotificationListenerBridge : NSObject
21 // Initialize the PasteboardNotificationListenerBridge with |delegate| which
22 // must not be null.
23 - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate
24     NS_DESIGNATED_INITIALIZER;
26 - (instancetype)init NS_UNAVAILABLE;
28 @end
30 @implementation PasteboardNotificationListenerBridge {
31   ClipboardRecentContentIOS* _delegate;
34 - (instancetype)init {
35   NOTREACHED();
36   return nil;
39 - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate {
40   DCHECK(delegate);
41   self = [super init];
42   if (self) {
43     _delegate = delegate;
44     [[NSNotificationCenter defaultCenter]
45         addObserver:self
46            selector:@selector(pasteboardChangedNotification:)
47                name:UIPasteboardChangedNotification
48              object:[UIPasteboard generalPasteboard]];
49     [[NSNotificationCenter defaultCenter]
50         addObserver:self
51            selector:@selector(pasteboardChangedNotification:)
52                name:UIApplicationDidBecomeActiveNotification
53              object:nil];
54   }
55   return self;
58 - (void)dealloc {
59   [[NSNotificationCenter defaultCenter] removeObserver:self];
60   [super dealloc];
63 - (void)pasteboardChangedNotification:(NSNotification*)notification {
64   if (_delegate) {
65     _delegate->PasteboardChanged();
66   }
69 - (void)disconnect {
70   _delegate = nullptr;
73 @end
75 namespace {
76 // Key used to store the pasteboard's current change count. If when resuming
77 // chrome the pasteboard's change count is different from the stored one, then
78 // it means that the pasteboard's content has changed.
79 NSString* kPasteboardChangeCountKey = @"PasteboardChangeCount";
80 // Key used to store the last date at which it was detected that the pasteboard
81 // changed. It is used to evaluate the age of the pasteboard's content.
82 NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate";
83 // Key used to store the
84 NSString* kSuppressedPasteboardEntryCountKey = @"PasteboardSupressedEntryCount";
85 base::TimeDelta kMaximumAgeOfClipboard = base::TimeDelta::FromHours(3);
86 // Schemes accepted by the ClipboardRecentContentIOS.
87 const char* kAuthorizedSchemes[] = {
88     url::kHttpScheme,
89     url::kHttpsScheme,
90     url::kDataScheme,
91     url::kAboutScheme,
93 }  // namespace
95 bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) const {
96   DCHECK(url);
97   if (GetClipboardContentAge() > kMaximumAgeOfClipboard ||
98       [UIPasteboard generalPasteboard].changeCount ==
99           suppressedPasteboardEntryCount_) {
100     return false;
101   }
103   if (urlFromPasteboardCache_.is_valid()) {
104     *url = urlFromPasteboardCache_;
105     return true;
106   }
107   return false;
110 base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const {
111   return base::TimeDelta::FromSeconds(
112       static_cast<int64>(-[lastPasteboardChangeDate_ timeIntervalSinceNow]));
115 void ClipboardRecentContentIOS::SuppressClipboardContent() {
116   suppressedPasteboardEntryCount_ =
117       [UIPasteboard generalPasteboard].changeCount;
118   SaveToUserDefaults();
121 void ClipboardRecentContentIOS::PasteboardChanged() {
122   urlFromPasteboardCache_ = URLFromPasteboard();
123   if (!urlFromPasteboardCache_.is_empty()) {
124     base::RecordAction(
125         base::UserMetricsAction("MobileOmniboxClipboardChanged"));
126   }
127   lastPasteboardChangeDate_.reset([[NSDate date] retain]);
128   lastPasteboardChangeCount_ = [UIPasteboard generalPasteboard].changeCount;
129   if (lastPasteboardChangeCount_ != suppressedPasteboardEntryCount_) {
130     suppressedPasteboardEntryCount_ = NSIntegerMax;
131   }
134 ClipboardRecentContentIOS::ClipboardRecentContentIOS(
135     const std::string& application_scheme)
136     : application_scheme_(application_scheme) {
137   Init(base::TimeDelta::FromMilliseconds(base::SysInfo::Uptime()));
140 ClipboardRecentContentIOS::ClipboardRecentContentIOS(
141     const std::string& application_scheme,
142     base::TimeDelta uptime)
143     : application_scheme_(application_scheme) {
144   Init(uptime);
147 void ClipboardRecentContentIOS::Init(base::TimeDelta uptime) {
148   lastPasteboardChangeCount_ = NSIntegerMax;
149   suppressedPasteboardEntryCount_ = NSIntegerMax;
150   urlFromPasteboardCache_ = URLFromPasteboard();
151   LoadFromUserDefaults();
153   // On iOS 7 (unlike on iOS 8, despite what the documentation says), the change
154   // count is reset when the device is rebooted.
155   if (uptime < GetClipboardContentAge() &&
156       !base::ios::IsRunningOnIOS8OrLater()) {
157     if ([UIPasteboard generalPasteboard].changeCount == 0) {
158       // The user hasn't pasted anything in the clipboard since the device's
159       // reboot. |PasteboardChanged| isn't called because it would update
160       // |lastPasteboardChangeData_|, and record metrics.
161       lastPasteboardChangeCount_ = 0;
162       if (suppressedPasteboardEntryCount_ != NSIntegerMax) {
163         // If the last time Chrome was running the pasteboard was suppressed,
164         // and the user  has not copied anything since the device launched, then
165         // supress this entry.
166         suppressedPasteboardEntryCount_ = 0;
167       }
168       SaveToUserDefaults();
169     } else {
170       // The user pasted something in the clipboard since the device's reboot.
171       PasteboardChanged();
172     }
173   } else {
174     NSInteger changeCount = [UIPasteboard generalPasteboard].changeCount;
175     if (changeCount != lastPasteboardChangeCount_) {
176       PasteboardChanged();
177     }
178   }
179   // Makes sure |lastPasteboardChangeCount_| was properly initialized.
180   DCHECK_NE(lastPasteboardChangeCount_, NSIntegerMax);
181   notificationBridge_.reset(
182       [[PasteboardNotificationListenerBridge alloc] initWithDelegate:this]);
185 ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {
186   [notificationBridge_ disconnect];
189 GURL ClipboardRecentContentIOS::URLFromPasteboard() {
190   const std::string clipboard =
191       base::SysNSStringToUTF8([[UIPasteboard generalPasteboard] string]);
192   GURL gurl = GURL(clipboard);
193   if (gurl.is_valid()) {
194     for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) {
195       if (gurl.SchemeIs(kAuthorizedSchemes[i])) {
196         return gurl;
197       }
198     }
199     if (!application_scheme_.empty() &&
200         gurl.SchemeIs(application_scheme_.c_str())) {
201       return gurl;
202     }
203   }
204   return GURL::EmptyGURL();
207 void ClipboardRecentContentIOS::LoadFromUserDefaults() {
208   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
210   lastPasteboardChangeCount_ =
211       [defaults integerForKey:kPasteboardChangeCountKey];
212   lastPasteboardChangeDate_.reset(
213       [[defaults objectForKey:kPasteboardChangeDateKey] retain]);
215   if ([[[defaults dictionaryRepresentation] allKeys]
216           containsObject:kSuppressedPasteboardEntryCountKey]) {
217     suppressedPasteboardEntryCount_ =
218         [defaults integerForKey:kSuppressedPasteboardEntryCountKey];
219   } else {
220     suppressedPasteboardEntryCount_ = NSIntegerMax;
221   }
223   DCHECK(!lastPasteboardChangeDate_ ||
224          [lastPasteboardChangeDate_ isKindOfClass:[NSDate class]]);
227 void ClipboardRecentContentIOS::SaveToUserDefaults() {
228   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
229   [defaults setInteger:lastPasteboardChangeCount_
230                 forKey:kPasteboardChangeCountKey];
231   [defaults setObject:lastPasteboardChangeDate_
232                forKey:kPasteboardChangeDateKey];
233   [defaults setInteger:suppressedPasteboardEntryCount_
234                 forKey:kSuppressedPasteboardEntryCountKey];