clipboard_recent_content_ios.mm 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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.
  4. #import "components/open_from_clipboard/clipboard_recent_content_ios.h"
  5. #import <CommonCrypto/CommonDigest.h>
  6. #import <UIKit/UIKit.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include "base/metrics/user_metrics.h"
  10. #include "base/notreached.h"
  11. #include "base/strings/sys_string_conversions.h"
  12. #include "base/system/sys_info.h"
  13. #include "base/threading/sequenced_task_runner_handle.h"
  14. #import "components/open_from_clipboard/clipboard_recent_content_impl_ios.h"
  15. #import "net/base/mac/url_conversions.h"
  16. #include "url/gurl.h"
  17. #include "url/url_constants.h"
  18. #if !defined(__has_feature) || !__has_feature(objc_arc)
  19. #error "This file requires ARC support."
  20. #endif
  21. namespace {
  22. // Schemes accepted by the ClipboardRecentContentIOS.
  23. const char* kAuthorizedSchemes[] = {
  24. url::kHttpScheme, url::kHttpsScheme, url::kDataScheme, url::kAboutScheme,
  25. };
  26. // Get the list of authorized schemes.
  27. NSSet<NSString*>* getAuthorizedSchemeList(
  28. const std::string& application_scheme) {
  29. NSMutableSet<NSString*>* schemes = [NSMutableSet set];
  30. for (size_t i = 0; i < std::size(kAuthorizedSchemes); ++i) {
  31. [schemes addObject:base::SysUTF8ToNSString(kAuthorizedSchemes[i])];
  32. }
  33. if (!application_scheme.empty()) {
  34. [schemes addObject:base::SysUTF8ToNSString(application_scheme)];
  35. }
  36. return [schemes copy];
  37. }
  38. ContentType ContentTypeFromClipboardContentType(ClipboardContentType type) {
  39. switch (type) {
  40. case ClipboardContentType::URL:
  41. return ContentTypeURL;
  42. case ClipboardContentType::Text:
  43. return ContentTypeText;
  44. case ClipboardContentType::Image:
  45. return ContentTypeImage;
  46. }
  47. }
  48. ClipboardContentType ClipboardContentTypeFromContentType(ContentType type) {
  49. if ([type isEqualToString:ContentTypeURL]) {
  50. return ClipboardContentType::URL;
  51. } else if ([type isEqualToString:ContentTypeText]) {
  52. return ClipboardContentType::Text;
  53. } else if ([type isEqualToString:ContentTypeImage]) {
  54. return ClipboardContentType::Image;
  55. }
  56. NOTREACHED();
  57. return ClipboardContentType::Text;
  58. }
  59. } // namespace
  60. @interface ClipboardRecentContentDelegateImpl
  61. : NSObject<ClipboardRecentContentDelegate>
  62. @end
  63. @implementation ClipboardRecentContentDelegateImpl
  64. - (void)onClipboardChanged {
  65. base::RecordAction(base::UserMetricsAction("MobileOmniboxClipboardChanged"));
  66. }
  67. @end
  68. ClipboardRecentContentIOS::ClipboardRecentContentIOS(
  69. const std::string& application_scheme,
  70. NSUserDefaults* group_user_defaults)
  71. : ClipboardRecentContentIOS([[ClipboardRecentContentImplIOS alloc]
  72. initWithMaxAge:MaximumAgeOfClipboard().InSecondsF()
  73. authorizedSchemes:getAuthorizedSchemeList(application_scheme)
  74. userDefaults:group_user_defaults
  75. delegate:[[ClipboardRecentContentDelegateImpl alloc]
  76. init]]) {}
  77. ClipboardRecentContentIOS::ClipboardRecentContentIOS(
  78. ClipboardRecentContentImplIOS* implementation) {
  79. implementation_ = implementation;
  80. }
  81. absl::optional<GURL> ClipboardRecentContentIOS::GetRecentURLFromClipboard() {
  82. NSURL* url_from_pasteboard = [implementation_ recentURLFromClipboard];
  83. GURL converted_url = net::GURLWithNSURL(url_from_pasteboard);
  84. if (!converted_url.is_valid()) {
  85. return absl::nullopt;
  86. }
  87. return converted_url;
  88. }
  89. absl::optional<std::u16string>
  90. ClipboardRecentContentIOS::GetRecentTextFromClipboard() {
  91. NSString* text_from_pasteboard = [implementation_ recentTextFromClipboard];
  92. if (!text_from_pasteboard) {
  93. return absl::nullopt;
  94. }
  95. return base::SysNSStringToUTF16(text_from_pasteboard);
  96. }
  97. bool ClipboardRecentContentIOS::HasRecentImageFromClipboard() {
  98. return GetRecentImageFromClipboardInternal().has_value();
  99. }
  100. void ClipboardRecentContentIOS::HasRecentContentFromClipboard(
  101. std::set<ClipboardContentType> types,
  102. HasDataCallback callback) {
  103. __block HasDataCallback callback_for_block = std::move(callback);
  104. NSMutableSet<ContentType>* ios_types = [NSMutableSet set];
  105. for (ClipboardContentType type : types) {
  106. [ios_types addObject:ContentTypeFromClipboardContentType(type)];
  107. }
  108. // The iOS methods for checking clipboard content call their callbacks on an
  109. // arbitrary thread. As Objective-C doesn't have very good thread-management
  110. // techniques, make sure this method calls its callback on the same thread
  111. // that it was called on.
  112. scoped_refptr<base::SequencedTaskRunner> task_runner =
  113. base::SequencedTaskRunnerHandle::Get();
  114. [implementation_
  115. hasContentMatchingTypes:ios_types
  116. completionHandler:^(NSSet<ContentType>* results) {
  117. std::set<ClipboardContentType> matching_types;
  118. for (ContentType type in results) {
  119. matching_types.insert(
  120. ClipboardContentTypeFromContentType(type));
  121. }
  122. task_runner->PostTask(
  123. FROM_HERE, base::BindOnce(^{
  124. std::move(callback_for_block).Run(matching_types);
  125. }));
  126. }];
  127. }
  128. // This value will be nullopt during the brief period
  129. // when the clipboard is updating its cache, which is triggered by a
  130. // pasteboardDidChange notification. It may also be nullopt if the app decides
  131. // it should not return the value of the clipboard, for example if the current
  132. // clipboard contents are too old.
  133. absl::optional<std::set<ClipboardContentType>>
  134. ClipboardRecentContentIOS::GetCachedClipboardContentTypes() {
  135. NSSet<ContentType>* current_content_types =
  136. [implementation_ cachedClipboardContentTypes];
  137. if (!current_content_types) {
  138. return absl::nullopt;
  139. }
  140. std::set<ClipboardContentType> current_content_types_ios;
  141. for (ContentType type in current_content_types) {
  142. current_content_types_ios.insert(ClipboardContentTypeFromContentType(type));
  143. }
  144. return current_content_types_ios;
  145. }
  146. void ClipboardRecentContentIOS::GetRecentURLFromClipboard(
  147. GetRecentURLCallback callback) {
  148. __block GetRecentURLCallback callback_for_block = std::move(callback);
  149. // The iOS methods for checking clipboard content call their callbacks on an
  150. // arbitrary thread. As Objective-C doesn't have very good thread-management
  151. // techniques, make sure this method calls its callback on the same thread
  152. // that it was called on.
  153. scoped_refptr<base::SequencedTaskRunner> task_runner =
  154. base::SequencedTaskRunnerHandle::Get();
  155. [implementation_ recentURLFromClipboardAsync:^(NSURL* url) {
  156. GURL converted_url = net::GURLWithNSURL(url);
  157. if (!converted_url.is_valid()) {
  158. task_runner->PostTask(FROM_HERE, base::BindOnce(^{
  159. std::move(callback_for_block).Run(absl::nullopt);
  160. }));
  161. return;
  162. }
  163. task_runner->PostTask(FROM_HERE, base::BindOnce(^{
  164. std::move(callback_for_block).Run(converted_url);
  165. }));
  166. }];
  167. }
  168. void ClipboardRecentContentIOS::GetRecentTextFromClipboard(
  169. GetRecentTextCallback callback) {
  170. __block GetRecentTextCallback callback_for_block = std::move(callback);
  171. // The iOS methods for checking clipboard content call their callbacks on an
  172. // arbitrary thread. As Objective-C doesn't have very good thread-management
  173. // techniques, make sure this method calls its callback on the same thread
  174. // that it was called on.
  175. scoped_refptr<base::SequencedTaskRunner> task_runner =
  176. base::SequencedTaskRunnerHandle::Get();
  177. [implementation_ recentTextFromClipboardAsync:^(NSString* text) {
  178. if (!text) {
  179. task_runner->PostTask(FROM_HERE, base::BindOnce(^{
  180. std::move(callback_for_block).Run(absl::nullopt);
  181. }));
  182. return;
  183. }
  184. task_runner->PostTask(
  185. FROM_HERE, base::BindOnce(^{
  186. std::move(callback_for_block).Run(base::SysNSStringToUTF16(text));
  187. }));
  188. }];
  189. }
  190. void ClipboardRecentContentIOS::GetRecentImageFromClipboard(
  191. GetRecentImageCallback callback) {
  192. __block GetRecentImageCallback callback_for_block = std::move(callback);
  193. // The iOS methods for checking clipboard content call their callbacks on an
  194. // arbitrary thread. As Objective-C doesn't have very good thread-management
  195. // techniques, make sure this method calls its callback on the same thread
  196. // that it was called on.
  197. scoped_refptr<base::SequencedTaskRunner> task_runner =
  198. base::SequencedTaskRunnerHandle::Get();
  199. [implementation_ recentImageFromClipboardAsync:^(UIImage* image) {
  200. if (!image) {
  201. task_runner->PostTask(FROM_HERE, base::BindOnce(^{
  202. std::move(callback_for_block).Run(absl::nullopt);
  203. }));
  204. return;
  205. }
  206. task_runner->PostTask(
  207. FROM_HERE, base::BindOnce(^{
  208. std::move(callback_for_block).Run(gfx::Image(image));
  209. }));
  210. }];
  211. }
  212. ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {}
  213. base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const {
  214. return base::Seconds(
  215. static_cast<int64_t>([implementation_ clipboardContentAge]));
  216. }
  217. void ClipboardRecentContentIOS::SuppressClipboardContent() {
  218. [implementation_ suppressClipboardContent];
  219. }
  220. void ClipboardRecentContentIOS::ClearClipboardContent() {
  221. NOTIMPLEMENTED();
  222. return;
  223. }
  224. absl::optional<gfx::Image>
  225. ClipboardRecentContentIOS::GetRecentImageFromClipboardInternal() {
  226. UIImage* image_from_pasteboard = [implementation_ recentImageFromClipboard];
  227. if (!image_from_pasteboard) {
  228. return absl::nullopt;
  229. }
  230. return gfx::Image(image_from_pasteboard);
  231. }