Roll src/third_party/WebKit c8d1660:ae3684d (svn 197337:197343)
[chromium-blink-merge.git] / components / open_from_clipboard / clipboard_recent_content_ios.mm
blob1d91586208b49b5bb4bccf3f5795277df68514c7
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 #include "components/open_from_clipboard/clipboard_recent_content_ios.h"
7 #import <UIKit/UIKit.h>
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "base/memory/singleton.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 ClipboardRecentContent* ClipboardRecentContent::GetInstance() {
19   return ClipboardRecentContentIOS::GetInstance();
22 // Bridge that forwards pasteboard change notifications to its delegate.
23 @interface PasteboardNotificationListenerBridge : NSObject {
24   ClipboardRecentContentIOS* _delegate;
26 @end
28 @implementation PasteboardNotificationListenerBridge
30 - (id)initWithDelegate:(ClipboardRecentContentIOS*)delegate {
31   DCHECK(delegate);
32   self = [super init];
33   if (self) {
34     _delegate = delegate;
35     [[NSNotificationCenter defaultCenter]
36         addObserver:self
37            selector:@selector(pasteboardChangedNotification:)
38                name:UIPasteboardChangedNotification
39              object:[UIPasteboard generalPasteboard]];
40     [[NSNotificationCenter defaultCenter]
41         addObserver:self
42            selector:@selector(pasteboardChangedNotification:)
43                name:UIApplicationDidBecomeActiveNotification
44              object:nil];
45   }
46   return self;
49 - (void)dealloc {
50   [[NSNotificationCenter defaultCenter]
51       removeObserver:self
52                 name:UIPasteboardChangedNotification
53               object:[UIPasteboard generalPasteboard]];
54   [[NSNotificationCenter defaultCenter]
55       removeObserver:self
56                 name:UIApplicationDidBecomeActiveNotification
57               object:[UIPasteboard generalPasteboard]];
58   [super dealloc];
61 - (void)pasteboardChangedNotification:(NSNotification*)notification {
62   _delegate->PasteboardChanged();
65 @end
67 namespace {
68 // Key used to store the pasteboard's current change count. If when resuming
69 // chrome the pasteboard's change count is different from the stored one, then
70 // it means that the pasteboard's content has changed.
71 NSString* kPasteboardChangeCountKey = @"PasteboardChangeCount";
72 // Key used to store the last date at which it was detected that the pasteboard
73 // changed. It is used to evaluate the age of the pasteboard's content.
74 NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate";
75 base::TimeDelta kMaximumAgeOfClipboard = base::TimeDelta::FromHours(3);
76 // Schemes accepted by the ClipboardRecentContentIOS.
77 const char* kAuthorizedSchemes[] = {
78     url::kHttpScheme,
79     url::kHttpsScheme,
80     url::kDataScheme,
81     url::kAboutScheme,
83 }  // namespace
85 ClipboardRecentContentIOS* ClipboardRecentContentIOS::GetInstance() {
86   return Singleton<ClipboardRecentContentIOS>::get();
89 bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) const {
90   DCHECK(url);
91   if (GetClipboardContentAge() > kMaximumAgeOfClipboard) {
92     return false;
93   }
94   if (urlFromPasteboardCache_.is_valid()) {
95     *url = urlFromPasteboardCache_;
96     return true;
97   }
98   return false;
101 base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const {
102   return base::TimeDelta::FromSeconds(
103       static_cast<int64>(-[lastPasteboardChangeDate_ timeIntervalSinceNow]));
106 void ClipboardRecentContentIOS::PasteboardChanged() {
107   urlFromPasteboardCache_ = URLFromPasteboard();
108   if (!urlFromPasteboardCache_.is_empty()) {
109     base::RecordAction(
110         base::UserMetricsAction("MobileOmniboxClipboardChanged"));
111   }
112   lastPasteboardChangeDate_.reset([[NSDate date] retain]);
113   lastPasteboardChangeCount_ = [UIPasteboard generalPasteboard].changeCount;
114   SaveToUserDefaults();
117 ClipboardRecentContentIOS::ClipboardRecentContentIOS()
118     : ClipboardRecentContent() {
119   urlFromPasteboardCache_ = URLFromPasteboard();
120   LoadFromUserDefaults();
121   // The pasteboard's changeCount is reset to zero when the device is restarted.
122   // This means that even if |changeCount| hasn't changed, the pasteboard
123   // content could have changed. In order to avoid missing pasteboard changes,
124   // the changeCount is reset if the device has restarted.
125   NSInteger changeCount = [UIPasteboard generalPasteboard].changeCount;
126   if (changeCount != lastPasteboardChangeCount_ ||
127       DeviceRestartedSincePasteboardChanged()) {
128     PasteboardChanged();
129   }
130   notificationBridge_.reset(
131       [[PasteboardNotificationListenerBridge alloc] initWithDelegate:this]);
134 ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {
137 GURL ClipboardRecentContentIOS::URLFromPasteboard() {
138   const std::string clipboard =
139       base::SysNSStringToUTF8([[UIPasteboard generalPasteboard] string]);
140   GURL gurl = GURL(clipboard);
141   if (gurl.is_valid()) {
142     for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) {
143       if (gurl.SchemeIs(kAuthorizedSchemes[i])) {
144         return gurl;
145       }
146     }
147     if (!application_scheme_.empty() &&
148         gurl.SchemeIs(application_scheme_.c_str())) {
149       return gurl;
150     }
151   }
152   return GURL::EmptyGURL();
155 void ClipboardRecentContentIOS::LoadFromUserDefaults() {
156   lastPasteboardChangeCount_ = [[NSUserDefaults standardUserDefaults]
157       integerForKey:kPasteboardChangeCountKey];
158   lastPasteboardChangeDate_.reset([[[NSUserDefaults standardUserDefaults]
159       objectForKey:kPasteboardChangeDateKey] retain]);
160   DCHECK(!lastPasteboardChangeDate_ ||
161          [lastPasteboardChangeDate_ isKindOfClass:[NSDate class]]);
164 void ClipboardRecentContentIOS::SaveToUserDefaults() {
165   [[NSUserDefaults standardUserDefaults] setInteger:lastPasteboardChangeCount_
166                                              forKey:kPasteboardChangeCountKey];
167   [[NSUserDefaults standardUserDefaults] setObject:lastPasteboardChangeDate_
168                                             forKey:kPasteboardChangeDateKey];
171 bool ClipboardRecentContentIOS::DeviceRestartedSincePasteboardChanged() {
172   base::TimeDelta timeSincePasteboardChange = GetClipboardContentAge();
173   base::TimeDelta timeSinceDeviceRestart =
174       base::TimeDelta::FromMilliseconds(base::SysInfo::Uptime());
175   return timeSincePasteboardChange > timeSinceDeviceRestart;