summaryrefslogtreecommitdiffstats
path: root/components/open_from_clipboard/clipboard_recent_content_ios.mm
blob: 9ec6f0794d838ba970caeee98e846539f679b7b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2015 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.

#include "components/open_from_clipboard/clipboard_recent_content_ios.h"

#import <UIKit/UIKit.h>

#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/sys_string_conversions.h"
#include "url/gurl.h"
#include "url/url_constants.h"

ClipboardRecentContent* ClipboardRecentContent::GetInstance() {
  return ClipboardRecentContentIOS::GetInstance();
}

// Bridge that forwards pasteboard change notifications to its delegate.
@interface PasteboardNotificationListenerBridge : NSObject {
  ClipboardRecentContentIOS* _delegate;
}
@end

@implementation PasteboardNotificationListenerBridge

- (id)initWithDelegate:(ClipboardRecentContentIOS*)delegate {
  DCHECK(delegate);
  self = [super init];
  if (self) {
    _delegate = delegate;
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(pasteboardChangedNotification:)
               name:UIPasteboardChangedNotification
             object:[UIPasteboard generalPasteboard]];
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(pasteboardChangedNotification:)
               name:UIApplicationDidBecomeActiveNotification
             object:nil];
  }
  return self;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter]
      removeObserver:self
                name:UIPasteboardChangedNotification
              object:[UIPasteboard generalPasteboard]];
  [[NSNotificationCenter defaultCenter]
      removeObserver:self
                name:UIApplicationDidBecomeActiveNotification
              object:[UIPasteboard generalPasteboard]];
  [super dealloc];
}

- (void)pasteboardChangedNotification:(NSNotification*)notification {
  _delegate->PasteboardChanged();
}

@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";
NSTimeInterval kMaximumAgeOfClipboardInSeconds = 6 * 60 * 60;
// Schemes accepted by the ClipboardRecentContentIOS.
const char* kAuthorizedSchemes[] = {
    url::kHttpScheme,
    url::kHttpsScheme,
    url::kDataScheme,
    url::kAboutScheme,
};
}  // namespace

ClipboardRecentContentIOS* ClipboardRecentContentIOS::GetInstance() {
  return Singleton<ClipboardRecentContentIOS>::get();
}

bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) const {
  DCHECK(url);
  if (-[lastPasteboardChangeDate_ timeIntervalSinceNow] >
      kMaximumAgeOfClipboardInSeconds) {
    return false;
  }
  if (urlFromPasteboardCache_.is_valid()) {
    *url = urlFromPasteboardCache_;
    return true;
  }
  return false;
}

void ClipboardRecentContentIOS::PasteboardChanged() {
  if ([UIPasteboard generalPasteboard].changeCount !=
      lastPasteboardChangeCount_) {
    urlFromPasteboardCache_ = URLFromPasteboard();
    if (!urlFromPasteboardCache_.is_empty()) {
      base::RecordAction(
          base::UserMetricsAction("MobileOmniboxClipboardChanged"));
    }
    lastPasteboardChangeDate_.reset([[NSDate date] retain]);
    lastPasteboardChangeCount_ = [UIPasteboard generalPasteboard].changeCount;
    SaveToUserDefaults();
  }
}

ClipboardRecentContentIOS::ClipboardRecentContentIOS()
    : ClipboardRecentContent() {
  urlFromPasteboardCache_ = URLFromPasteboard();
  LoadFromUserDefaults();
  if ([UIPasteboard generalPasteboard].changeCount !=
      lastPasteboardChangeCount_) {
    PasteboardChanged();
  }
  notificationBridge_.reset(
      [[PasteboardNotificationListenerBridge alloc] initWithDelegate:this]);
}

ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {
}

GURL ClipboardRecentContentIOS::URLFromPasteboard() {
  const std::string clipboard =
      base::SysNSStringToUTF8([[UIPasteboard generalPasteboard] 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;
    }
  }
  return GURL::EmptyGURL();
}

void ClipboardRecentContentIOS::LoadFromUserDefaults() {
  lastPasteboardChangeCount_ = [[NSUserDefaults standardUserDefaults]
      integerForKey:kPasteboardChangeCountKey];
  lastPasteboardChangeDate_.reset([[[NSUserDefaults standardUserDefaults]
      objectForKey:kPasteboardChangeDateKey] retain]);
  DCHECK(!lastPasteboardChangeDate_ ||
         [lastPasteboardChangeDate_ isKindOfClass:[NSDate class]]);
}

void ClipboardRecentContentIOS::SaveToUserDefaults() {
  [[NSUserDefaults standardUserDefaults] setInteger:lastPasteboardChangeCount_
                                             forKey:kPasteboardChangeCountKey];
  [[NSUserDefaults standardUserDefaults] setObject:lastPasteboardChangeDate_
                                            forKey:kPasteboardChangeDateKey];
}