summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjif <jif@chromium.org>2015-02-19 07:40:12 -0800
committerCommit bot <commit-bot@chromium.org>2015-02-19 15:41:00 +0000
commit9021d87d62b29e0b1ee4625ad0006b500cc3f683 (patch)
tree26b9d87c78c7919c238efac100981e7833097957
parentcdc03121d7c798a87c4c90fd16981065ed06c5db (diff)
downloadchromium_src-9021d87d62b29e0b1ee4625ad0006b500cc3f683.zip
chromium_src-9021d87d62b29e0b1ee4625ad0006b500cc3f683.tar.gz
chromium_src-9021d87d62b29e0b1ee4625ad0006b500cc3f683.tar.bz2
Upstream iOS' Open from Clipboard component.
The component's goal is to provide omnibox suggestions based on the clipboard's content. BUG=None. Review URL: https://codereview.chromium.org/930323003 Cr-Commit-Position: refs/heads/master@{#317055}
-rw-r--r--build/ios/grit_whitelist.txt1
-rw-r--r--components/OWNERS3
-rw-r--r--components/components.gyp1
-rw-r--r--components/components_strings.grd1
-rw-r--r--components/open_from_clipboard.gypi28
-rw-r--r--components/open_from_clipboard/DEPS5
-rw-r--r--components/open_from_clipboard/OWNERS1
-rw-r--r--components/open_from_clipboard/clipboard_recent_content.h43
-rw-r--r--components/open_from_clipboard/clipboard_recent_content_ios.h53
-rw-r--r--components/open_from_clipboard/clipboard_recent_content_ios.mm162
-rw-r--r--components/open_from_clipboard/clipboard_url_provider.cc59
-rw-r--r--components/open_from_clipboard/clipboard_url_provider.h27
-rw-r--r--components/open_from_clipboard_strings.grdp6
-rw-r--r--tools/metrics/actions/actions.xml10
14 files changed, 400 insertions, 0 deletions
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index c1edfdf..ab1fb5d 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -719,6 +719,7 @@ IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE
IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE_AND_URL
IDS_LIBADDRESSINPUT_VILLAGE_TOWNSHIP
IDS_LIBADDRESSINPUT_ZIP_CODE_LABEL
+IDS_LINK_FROM_CLIPBOARD
IDS_LOGIN_DIALOG_OK_BUTTON_LABEL
IDS_LOGIN_DIALOG_PASSWORD_FIELD
IDS_LOGIN_DIALOG_TITLE
diff --git a/components/OWNERS b/components/OWNERS
index 6946515..d563137 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -145,6 +145,9 @@ per-file onc.gypi=gspencer@chromium.org
per-file onc.gypi=pneubeck@chromium.org
per-file onc.gypi=stevenjb@chromium.org
+per-file open_from_clipboard.gypi=jif@chromium.org
+per-file open_from_clipboard_strings.grdp=jif@chromium.org
+
per-file packed_ct_ev_whitelist.gypi=eranm@chromium.org
per-file packed_ct_ev_whitelist.gypi=rsleevi@chromium.org
diff --git a/components/components.gyp b/components/components.gyp
index 974d292..39e07f7 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -83,6 +83,7 @@
}],
['OS == "ios"', {
'includes': [
+ 'open_from_clipboard.gypi',
'webp_transcode.gypi',
],
}],
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 35d1c59..e48591a 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -174,6 +174,7 @@
<part file="dom_distiller_strings.grdp" />
<part file="error_page_strings.grdp" />
<part file="omnibox_strings.grdp" />
+ <part file="open_from_clipboard_strings.grdp" />
<part file="password_manager_strings.grdp" />
<part file="pdf_strings.grdp" />
<part file="policy_strings.grdp" />
diff --git a/components/open_from_clipboard.gypi b/components/open_from_clipboard.gypi
new file mode 100644
index 0000000..103e6d0
--- /dev/null
+++ b/components/open_from_clipboard.gypi
@@ -0,0 +1,28 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'open_from_clipboard',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../url/url.gyp:url_lib',
+ 'components_strings.gyp:components_strings',
+ 'omnibox',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'open_from_clipboard/clipboard_recent_content.h',
+ 'open_from_clipboard/clipboard_recent_content_ios.h',
+ 'open_from_clipboard/clipboard_recent_content_ios.mm',
+ 'open_from_clipboard/clipboard_url_provider.cc',
+ 'open_from_clipboard/clipboard_url_provider.h',
+ ],
+ },
+ ],
+}
diff --git a/components/open_from_clipboard/DEPS b/components/open_from_clipboard/DEPS
new file mode 100644
index 0000000..30a8913
--- /dev/null
+++ b/components/open_from_clipboard/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+components/omnibox",
+ "+grit/components_strings.h",
+ "+ui/base/l10n",
+]
diff --git a/components/open_from_clipboard/OWNERS b/components/open_from_clipboard/OWNERS
new file mode 100644
index 0000000..48eb126
--- /dev/null
+++ b/components/open_from_clipboard/OWNERS
@@ -0,0 +1 @@
+jif@chromium.org \ No newline at end of file
diff --git a/components/open_from_clipboard/clipboard_recent_content.h b/components/open_from_clipboard/clipboard_recent_content.h
new file mode 100644
index 0000000..31c274c
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_H_
+#define COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+class GURL;
+
+// Helper class returning an URL if the content of the clipboard can be turned
+// into an URL, and if it estimates that the content of the clipboard is not too
+// old.
+class ClipboardRecentContent {
+ public:
+ // Returns an instance of the ClipboardContent singleton.
+ static ClipboardRecentContent* GetInstance();
+
+ // Returns true if the clipboard contains a recent URL, and copies it in
+ // |url|. Otherwise, returns false. |url| must not be null.
+ virtual bool GetRecentURLFromClipboard(GURL* url) const = 0;
+
+ // Sets which URL scheme this app can be opened with. Used by
+ // ClipboardRecentContent to determine whether or not the clipboard contains
+ // a relevant URL. |application_scheme| may be empty.
+ void set_application_scheme(const std::string& application_scheme) {
+ application_scheme_ = application_scheme;
+ }
+
+ protected:
+ ClipboardRecentContent() {}
+ virtual ~ClipboardRecentContent() {}
+ // Contains the URL scheme opening the app. May be empty.
+ std::string application_scheme_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ClipboardRecentContent);
+};
+
+#endif // COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_H_
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.h b/components/open_from_clipboard/clipboard_recent_content_ios.h
new file mode 100644
index 0000000..5c3ff46
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_IOS_H_
+#define COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_IOS_H_
+
+#include "base/mac/scoped_nsobject.h"
+#include "components/open_from_clipboard/clipboard_recent_content.h"
+#include "url/gurl.h"
+
+@class NSDate;
+@class PasteboardNotificationListenerBridge;
+
+template <typename T>
+struct DefaultSingletonTraits;
+
+// IOS implementation of ClipboardRecentContent
+class ClipboardRecentContentIOS : public ClipboardRecentContent {
+ public:
+ static ClipboardRecentContentIOS* GetInstance();
+ // Notifies that the content of the pasteboard may have changed.
+ void PasteboardChanged();
+
+ // ClipboardRecentContent implementation.
+ bool GetRecentURLFromClipboard(GURL* url) const override;
+
+ private:
+ friend struct DefaultSingletonTraits<ClipboardRecentContentIOS>;
+
+ ClipboardRecentContentIOS();
+ ~ClipboardRecentContentIOS() override;
+ // Loads information from the user defaults about the latest clipboard entry.
+ void LoadFromUserDefaults();
+ // Saves information to the user defaults about the latest clipboard entry.
+ void SaveToUserDefaults();
+ // Returns the URL contained in the clipboard (if any).
+ GURL URLFromPasteboard();
+
+ // The pasteboard's change count. Increases everytime the pasteboard changes.
+ NSInteger lastPasteboardChangeCount_;
+ // Estimation of the date when the pasteboard changed.
+ base::scoped_nsobject<NSDate> lastPasteboardChangeDate_;
+ // Cache of the GURL contained in the pasteboard (if any).
+ GURL urlFromPasteboardCache_;
+ // Bridge to receive notification when the pasteboard changes.
+ base::scoped_nsobject<PasteboardNotificationListenerBridge>
+ notificationBridge_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardRecentContentIOS);
+};
+
+#endif // COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_IOS_H_
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.mm b/components/open_from_clipboard/clipboard_recent_content_ios.mm
new file mode 100644
index 0000000..9ec6f07
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.mm
@@ -0,0 +1,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];
+}
diff --git a/components/open_from_clipboard/clipboard_url_provider.cc b/components/open_from_clipboard/clipboard_url_provider.cc
new file mode 100644
index 0000000..33fdf38
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_url_provider.cc
@@ -0,0 +1,59 @@
+// 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_url_provider.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/omnibox/autocomplete_input.h"
+#include "components/open_from_clipboard/clipboard_recent_content.h"
+#include "grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+// Score defining the priority of the suggestion. 1600 guarantees that the
+// suggestion will always be first. See table in autocomplete_provider.h.
+const int kClipboardURLProviderRelevance = 1600;
+} // namespace
+
+ClipboardURLProvider::ClipboardURLProvider(
+ ClipboardRecentContent* clipboard_recent_content)
+ : AutocompleteProvider(AutocompleteProvider::TYPE_SHORTCUTS),
+ clipboard_recent_content_(clipboard_recent_content) {
+ DCHECK(clipboard_recent_content_);
+}
+
+ClipboardURLProvider::~ClipboardURLProvider() {
+}
+
+void ClipboardURLProvider::Start(const AutocompleteInput& input,
+ bool minimal_changes,
+ bool called_due_to_focus) {
+ matches_.clear();
+ // Attempt to add an AutocompleteMatch only if the user has not entered
+ // anything in the omnibox.
+ if (input.cursor_position() == base::string16::npos) {
+ GURL url;
+ if (clipboard_recent_content_->GetRecentURLFromClipboard(&url)) {
+ DCHECK(url.is_valid());
+ AutocompleteMatch match(this, kClipboardURLProviderRelevance, false,
+ AutocompleteMatchType::NAVSUGGEST_PERSONALIZED);
+ match.allowed_to_be_default_match = true;
+ match.destination_url = url;
+ match.contents.assign(base::UTF8ToUTF16(url.spec()));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.contents.length(),
+ ACMatchClassification::URL, &match.contents_class);
+
+ match.description.assign(
+ l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
+ AutocompleteMatch::ClassifyLocationInString(
+ base::string16::npos, 0, match.description.length(),
+ ACMatchClassification::URL, &match.description_class);
+
+ matches_.push_back(match);
+ }
+ }
+ done_ = true;
+}
diff --git a/components/open_from_clipboard/clipboard_url_provider.h b/components/open_from_clipboard/clipboard_url_provider.h
new file mode 100644
index 0000000..38122cc
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_url_provider.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_URL_PROVIDER_H_
+#define COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_URL_PROVIDER_H_
+
+#include "components/omnibox/autocomplete_provider.h"
+
+class ClipboardRecentContent;
+
+// Autocomplete provider offering content based on the clipboard's content.
+class ClipboardURLProvider : public AutocompleteProvider {
+ public:
+ ClipboardURLProvider(ClipboardRecentContent* clipboard_recent_content);
+ void Start(const AutocompleteInput& input,
+ bool minimal_changes,
+ bool called_due_to_focus) override;
+
+ private:
+ ~ClipboardURLProvider() override;
+ ClipboardRecentContent* clipboard_recent_content_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClipboardURLProvider);
+};
+
+#endif // COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_URL_PROVIDER_H_
diff --git a/components/open_from_clipboard_strings.grdp b/components/open_from_clipboard_strings.grdp
new file mode 100644
index 0000000..c6ec2b5
--- /dev/null
+++ b/components/open_from_clipboard_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <message name="IDS_LINK_FROM_CLIPBOARD" desc="The label in the omnibox dropdown explaining that the link has been extracted from the user's clipboard. [Length: 21em]">
+ Link you copied
+ </message>
+</grit-part>
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index d222a02..95674aa 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -7592,6 +7592,16 @@ should be able to be added at any place in this file.
<description>Please enter the description of this user action.</description>
</action>
+<action name="MobileOmniboxClipboardChanged">
+ <owner>jif@chromium.org</owner>
+ <description>
+ Emitted when Chrome detects that the clipboard contains a new URL. This
+ occurs either when Chrome enters the foreground and notices that the content
+ of the clipboard changed, or when the clipboard changes while Chrome is in
+ the foreground.
+ </description>
+</action>
+
<action name="MobileOmniboxRefineSuggestion">
<owner>Please list the metric's owners. Add more owner tags as needed.</owner>
<description>Please enter the description of this user action.</description>