Commit fe977358 authored by jif's avatar jif Committed by Commit bot

Stop using UIPasteboardChangedNotification.

iOS10 does not trigger UIPasteboardChangedNotification notifications
on pasteboard change ( filed bug with Apple: rdr://27477065 ).
This CL is a workaround where we check if the pasteboard changed
when we attempt to display its content.

BUG=628592

Review-Url: https://codereview.chromium.org/2230983002
Cr-Commit-Position: refs/heads/master@{#412788}
parent 401ea0d7
...@@ -31,10 +31,7 @@ class ClipboardRecentContent { ...@@ -31,10 +31,7 @@ class ClipboardRecentContent {
// Returns true if the clipboard contains a recent URL that has not been // Returns true if the clipboard contains a recent URL that has not been
// supressed, and copies it in |url|. Otherwise, returns false. |url| must not // supressed, and copies it in |url|. Otherwise, returns false. |url| must not
// be null. // be null.
virtual bool GetRecentURLFromClipboard(GURL* url) const = 0; virtual bool GetRecentURLFromClipboard(GURL* url) = 0;
// Reports that the URL contained in the pasteboard was displayed.
virtual void RecentURLDisplayed() = 0;
// Returns how old the content of the clipboard is. // Returns how old the content of the clipboard is.
virtual base::TimeDelta GetClipboardContentAge() const = 0; virtual base::TimeDelta GetClipboardContentAge() const = 0;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
@class NSDate; @class NSDate;
@class NSUserDefaults; @class NSUserDefaults;
@class PasteboardNotificationListenerBridge; @class ApplicationDidBecomeActiveNotificationListenerBridge;
class ClipboardRecentContentIOSTest; class ClipboardRecentContentIOSTest;
...@@ -30,39 +30,29 @@ class ClipboardRecentContentIOS : public ClipboardRecentContent { ...@@ -30,39 +30,29 @@ class ClipboardRecentContentIOS : public ClipboardRecentContent {
NSUserDefaults* group_user_defaults); NSUserDefaults* group_user_defaults);
~ClipboardRecentContentIOS() override; ~ClipboardRecentContentIOS() override;
// Notifies that the content of the pasteboard may have changed. // If the content of the pasteboard has changed, updates the change count,
void PasteboardChanged(); // change date, and md5 of the latest pasteboard entry if necessary.
void UpdateIfNeeded();
// Checks if pasteboard changed since last time a pasteboard change was // Returns whether the pasteboard changed since the last time a pasteboard
// registered. // change was detected.
bool HasPasteboardChanged(base::TimeDelta uptime); bool HasPasteboardChanged() const;
// Gets the current URL in the clipboard. If the cache is out of date, updates
// it.
bool GetCurrentURLFromClipboard(GURL* url);
// Loads information from the user defaults about the latest pasteboard entry. // Loads information from the user defaults about the latest pasteboard entry.
void LoadFromUserDefaults(); void LoadFromUserDefaults();
// ClipboardRecentContent implementation. // ClipboardRecentContent implementation.
bool GetRecentURLFromClipboard(GURL* url) const override; bool GetRecentURLFromClipboard(GURL* url) override;
base::TimeDelta GetClipboardContentAge() const override; base::TimeDelta GetClipboardContentAge() const override;
void SuppressClipboardContent() override; void SuppressClipboardContent() override;
void RecentURLDisplayed() override;
protected:
// Returns the uptime. Override in tests to return custom value.
virtual base::TimeDelta Uptime() const;
private: private:
friend class ClipboardRecentContentIOSTest; friend class ClipboardRecentContentIOSTest;
// Helper constructor for testing. |uptime| is how long ago the device has
// started, while |application_scheme| has the same meaning as the public
// constructor.
ClipboardRecentContentIOS(const std::string& application_scheme,
base::TimeDelta uptime);
// Initializes the object. |uptime| is how long ago the device has started.
void Init(base::TimeDelta uptime);
// Saves information to the user defaults about the latest pasteboard entry. // Saves information to the user defaults about the latest pasteboard entry.
void SaveToUserDefaults(); void SaveToUserDefaults();
...@@ -75,14 +65,10 @@ class ClipboardRecentContentIOS : public ClipboardRecentContent { ...@@ -75,14 +65,10 @@ class ClipboardRecentContentIOS : public ClipboardRecentContent {
NSInteger last_pasteboard_change_count_; NSInteger last_pasteboard_change_count_;
// Estimation of the date when the pasteboard changed. // Estimation of the date when the pasteboard changed.
base::scoped_nsobject<NSDate> last_pasteboard_change_date_; base::scoped_nsobject<NSDate> last_pasteboard_change_date_;
// Estimation of the copy date of the last displayed URL.
base::scoped_nsobject<NSDate> last_displayed_pasteboard_entry_;
// MD5 hash of the last registered pasteboard entry. // MD5 hash of the last registered pasteboard entry.
base::scoped_nsobject<NSData> last_pasteboard_entry_md5_; base::scoped_nsobject<NSData> last_pasteboard_entry_md5_;
// Cache of the GURL contained in the pasteboard (if any). // Bridge to receive notifications when the application becomes active.
GURL url_from_pasteboard_cache_; base::scoped_nsobject<ApplicationDidBecomeActiveNotificationListenerBridge>
// Bridge to receive notification when the pasteboard changes.
base::scoped_nsobject<PasteboardNotificationListenerBridge>
notification_bridge_; notification_bridge_;
// The user defaults from the app group used to optimize the pasteboard change // The user defaults from the app group used to optimize the pasteboard change
// detection. // detection.
......
...@@ -18,11 +18,12 @@ ...@@ -18,11 +18,12 @@
#include "url/gurl.h" #include "url/gurl.h"
#include "url/url_constants.h" #include "url/url_constants.h"
// Bridge that forwards pasteboard change notifications to its delegate. // Bridge that forwards UIApplicationDidBecomeActiveNotification notifications
@interface PasteboardNotificationListenerBridge : NSObject // to its delegate.
@interface ApplicationDidBecomeActiveNotificationListenerBridge : NSObject
// Initialize the PasteboardNotificationListenerBridge with |delegate| which // Initialize the ApplicationDidBecomeActiveNotificationListenerBridge with
// must not be null. // |delegate| which must not be null.
- (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate - (instancetype)initWithDelegate:(ClipboardRecentContentIOS*)delegate
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
...@@ -30,7 +31,7 @@ ...@@ -30,7 +31,7 @@
@end @end
@implementation PasteboardNotificationListenerBridge { @implementation ApplicationDidBecomeActiveNotificationListenerBridge {
ClipboardRecentContentIOS* _delegate; ClipboardRecentContentIOS* _delegate;
} }
...@@ -44,11 +45,6 @@ ...@@ -44,11 +45,6 @@
self = [super init]; self = [super init];
if (self) { if (self) {
_delegate = delegate; _delegate = delegate;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(pasteboardChangedNotification:)
name:UIPasteboardChangedNotification
object:[UIPasteboard generalPasteboard]];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
addObserver:self addObserver:self
selector:@selector(didBecomeActive:) selector:@selector(didBecomeActive:)
...@@ -63,18 +59,10 @@ ...@@ -63,18 +59,10 @@
[super dealloc]; [super dealloc];
} }
- (void)pasteboardChangedNotification:(NSNotification*)notification {
if (_delegate) {
_delegate->PasteboardChanged();
}
}
- (void)didBecomeActive:(NSNotification*)notification { - (void)didBecomeActive:(NSNotification*)notification {
if (_delegate) { if (_delegate) {
_delegate->LoadFromUserDefaults(); _delegate->LoadFromUserDefaults();
if (_delegate->HasPasteboardChanged(base::SysInfo::Uptime())) { _delegate->UpdateIfNeeded();
_delegate->PasteboardChanged();
}
} }
} }
...@@ -95,9 +83,6 @@ NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate"; ...@@ -95,9 +83,6 @@ NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate";
// Key used to store the hash of the content of the pasteboard. Whenever the // Key used to store the hash of the content of the pasteboard. Whenever the
// hash changed, the pasteboard content is considered to have changed. // hash changed, the pasteboard content is considered to have changed.
NSString* kPasteboardEntryMD5Key = @"PasteboardEntryMD5"; NSString* kPasteboardEntryMD5Key = @"PasteboardEntryMD5";
// Key used to store the date of the latest pasteboard entry displayed in the
// omnibox. This is used to report metrics on pasteboard change.
NSString* kLastDisplayedPasteboardEntryKey = @"LastDisplayedPasteboardEntry";
base::TimeDelta kMaximumAgeOfClipboard = base::TimeDelta::FromHours(3); base::TimeDelta kMaximumAgeOfClipboard = base::TimeDelta::FromHours(3);
// Schemes accepted by the ClipboardRecentContentIOS. // Schemes accepted by the ClipboardRecentContentIOS.
const char* kAuthorizedSchemes[] = { const char* kAuthorizedSchemes[] = {
...@@ -121,14 +106,16 @@ NSData* WeakMD5FromNSString(NSString* string) { ...@@ -121,14 +106,16 @@ NSData* WeakMD5FromNSString(NSString* string) {
} // namespace } // namespace
bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) const { bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) {
DCHECK(url); DCHECK(url);
UpdateIfNeeded();
if (GetClipboardContentAge() > kMaximumAgeOfClipboard) { if (GetClipboardContentAge() > kMaximumAgeOfClipboard) {
return false; return false;
} }
if (url_from_pasteboard_cache_.is_valid()) { GURL url_from_pasteboard = URLFromPasteboard();
*url = url_from_pasteboard_cache_; if (url_from_pasteboard.is_valid()) {
*url = url_from_pasteboard;
return true; return true;
} }
return false; return false;
...@@ -147,12 +134,13 @@ void ClipboardRecentContentIOS::SuppressClipboardContent() { ...@@ -147,12 +134,13 @@ void ClipboardRecentContentIOS::SuppressClipboardContent() {
SaveToUserDefaults(); SaveToUserDefaults();
} }
void ClipboardRecentContentIOS::PasteboardChanged() { void ClipboardRecentContentIOS::UpdateIfNeeded() {
url_from_pasteboard_cache_ = URLFromPasteboard(); if (!HasPasteboardChanged())
if (!url_from_pasteboard_cache_.is_empty()) { return;
base::RecordAction(
base::UserMetricsAction("MobileOmniboxClipboardChanged")); base::RecordAction(base::UserMetricsAction("MobileOmniboxClipboardChanged"));
}
GURL url_from_pasteboard = URLFromPasteboard();
last_pasteboard_change_date_.reset([[NSDate date] retain]); last_pasteboard_change_date_.reset([[NSDate date] retain]);
last_pasteboard_change_count_ = [UIPasteboard generalPasteboard].changeCount; last_pasteboard_change_count_ = [UIPasteboard generalPasteboard].changeCount;
NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string]; NSString* pasteboard_string = [[UIPasteboard generalPasteboard] string];
...@@ -169,18 +157,19 @@ ClipboardRecentContentIOS::ClipboardRecentContentIOS( ...@@ -169,18 +157,19 @@ ClipboardRecentContentIOS::ClipboardRecentContentIOS(
NSUserDefaults* group_user_defaults) NSUserDefaults* group_user_defaults)
: application_scheme_(application_scheme), : application_scheme_(application_scheme),
shared_user_defaults_([group_user_defaults retain]) { shared_user_defaults_([group_user_defaults retain]) {
Init(base::SysInfo::Uptime()); last_pasteboard_change_count_ = NSIntegerMax;
} LoadFromUserDefaults();
ClipboardRecentContentIOS::ClipboardRecentContentIOS( UpdateIfNeeded();
const std::string& application_scheme,
base::TimeDelta uptime) // Makes sure |last_pasteboard_change_count_| was properly initialized.
: application_scheme_(application_scheme), DCHECK_NE(last_pasteboard_change_count_, NSIntegerMax);
shared_user_defaults_([[NSUserDefaults standardUserDefaults] retain]) { notification_bridge_.reset(
Init(uptime); [[ApplicationDidBecomeActiveNotificationListenerBridge alloc]
initWithDelegate:this]);
} }
bool ClipboardRecentContentIOS::HasPasteboardChanged(base::TimeDelta uptime) { bool ClipboardRecentContentIOS::HasPasteboardChanged() const {
// If |MD5Changed|, we know for sure there has been at least one pasteboard // If |MD5Changed|, we know for sure there has been at least one pasteboard
// copy since last time it was checked. // copy since last time it was checked.
// If the pasteboard content is still the same but the device was not // If the pasteboard content is still the same but the device was not
...@@ -192,7 +181,7 @@ bool ClipboardRecentContentIOS::HasPasteboardChanged(base::TimeDelta uptime) { ...@@ -192,7 +181,7 @@ bool ClipboardRecentContentIOS::HasPasteboardChanged(base::TimeDelta uptime) {
NSInteger change_count = [UIPasteboard generalPasteboard].changeCount; NSInteger change_count = [UIPasteboard generalPasteboard].changeCount;
bool change_count_changed = change_count != last_pasteboard_change_count_; bool change_count_changed = change_count != last_pasteboard_change_count_;
bool not_rebooted = uptime > GetClipboardContentAge(); bool not_rebooted = Uptime() > GetClipboardContentAge();
if (not_rebooted) if (not_rebooted)
return change_count_changed; return change_count_changed;
...@@ -206,27 +195,6 @@ bool ClipboardRecentContentIOS::HasPasteboardChanged(base::TimeDelta uptime) { ...@@ -206,27 +195,6 @@ bool ClipboardRecentContentIOS::HasPasteboardChanged(base::TimeDelta uptime) {
return md5_changed; return md5_changed;
} }
bool ClipboardRecentContentIOS::GetCurrentURLFromClipboard(GURL* url) {
if (HasPasteboardChanged(base::SysInfo::Uptime())) {
PasteboardChanged();
}
return GetRecentURLFromClipboard(url);
}
void ClipboardRecentContentIOS::Init(base::TimeDelta uptime) {
last_pasteboard_change_count_ = NSIntegerMax;
url_from_pasteboard_cache_ = URLFromPasteboard();
LoadFromUserDefaults();
if (HasPasteboardChanged(uptime))
PasteboardChanged();
// Makes sure |last_pasteboard_change_count_| was properly initialized.
DCHECK_NE(last_pasteboard_change_count_, NSIntegerMax);
notification_bridge_.reset(
[[PasteboardNotificationListenerBridge alloc] initWithDelegate:this]);
}
ClipboardRecentContentIOS::~ClipboardRecentContentIOS() { ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {
[notification_bridge_ disconnect]; [notification_bridge_ disconnect];
} }
...@@ -252,16 +220,6 @@ GURL ClipboardRecentContentIOS::URLFromPasteboard() { ...@@ -252,16 +220,6 @@ GURL ClipboardRecentContentIOS::URLFromPasteboard() {
return GURL::EmptyGURL(); return GURL::EmptyGURL();
} }
void ClipboardRecentContentIOS::RecentURLDisplayed() {
if ([last_pasteboard_change_date_
isEqualToDate:last_displayed_pasteboard_entry_.get()]) {
return;
}
base::RecordAction(base::UserMetricsAction("MobileOmniboxClipboardChanged"));
last_pasteboard_change_date_ = last_displayed_pasteboard_entry_;
SaveToUserDefaults();
}
void ClipboardRecentContentIOS::LoadFromUserDefaults() { void ClipboardRecentContentIOS::LoadFromUserDefaults() {
last_pasteboard_change_count_ = last_pasteboard_change_count_ =
[shared_user_defaults_ integerForKey:kPasteboardChangeCountKey]; [shared_user_defaults_ integerForKey:kPasteboardChangeCountKey];
...@@ -269,8 +227,6 @@ void ClipboardRecentContentIOS::LoadFromUserDefaults() { ...@@ -269,8 +227,6 @@ void ClipboardRecentContentIOS::LoadFromUserDefaults() {
[[shared_user_defaults_ objectForKey:kPasteboardChangeDateKey] retain]); [[shared_user_defaults_ objectForKey:kPasteboardChangeDateKey] retain]);
last_pasteboard_entry_md5_.reset( last_pasteboard_entry_md5_.reset(
[[shared_user_defaults_ objectForKey:kPasteboardEntryMD5Key] retain]); [[shared_user_defaults_ objectForKey:kPasteboardEntryMD5Key] retain]);
last_displayed_pasteboard_entry_.reset([[shared_user_defaults_
objectForKey:kLastDisplayedPasteboardEntryKey] retain]);
DCHECK(!last_pasteboard_change_date_ || DCHECK(!last_pasteboard_change_date_ ||
[last_pasteboard_change_date_ isKindOfClass:[NSDate class]]); [last_pasteboard_change_date_ isKindOfClass:[NSDate class]]);
...@@ -283,6 +239,8 @@ void ClipboardRecentContentIOS::SaveToUserDefaults() { ...@@ -283,6 +239,8 @@ void ClipboardRecentContentIOS::SaveToUserDefaults() {
forKey:kPasteboardChangeDateKey]; forKey:kPasteboardChangeDateKey];
[shared_user_defaults_ setObject:last_pasteboard_entry_md5_ [shared_user_defaults_ setObject:last_pasteboard_entry_md5_
forKey:kPasteboardEntryMD5Key]; forKey:kPasteboardEntryMD5Key];
[shared_user_defaults_ setObject:last_displayed_pasteboard_entry_ }
forKey:kLastDisplayedPasteboardEntryKey];
base::TimeDelta ClipboardRecentContentIOS::Uptime() const {
return base::SysInfo::Uptime();
} }
...@@ -39,11 +39,28 @@ void SetPasteboardContent(const char* data) { ...@@ -39,11 +39,28 @@ void SetPasteboardContent(const char* data) {
} }
const char kUnrecognizedURL[] = "ftp://foo/"; const char kUnrecognizedURL[] = "ftp://foo/";
const char kRecognizedURL[] = "http://bar/"; const char kRecognizedURL[] = "http://bar/";
const char kRecognizedURL2[] = "http://bar/2";
const char kAppSpecificURL[] = "test://qux/"; const char kAppSpecificURL[] = "test://qux/";
const char kAppSpecificScheme[] = "test"; const char kAppSpecificScheme[] = "test";
NSTimeInterval kSevenHours = 60 * 60 * 7; NSTimeInterval kSevenHours = 60 * 60 * 7;
} // namespace } // namespace
class ClipboardRecentContentIOSWithFakeUptime
: public ClipboardRecentContentIOS {
public:
ClipboardRecentContentIOSWithFakeUptime(const std::string& application_scheme,
NSUserDefaults* group_user_defaults)
: ClipboardRecentContentIOS(application_scheme, group_user_defaults) {}
// Sets the uptime.
void SetUptime(base::TimeDelta uptime) { uptime_ = uptime; }
protected:
base::TimeDelta Uptime() const override { return uptime_; }
private:
base::TimeDelta uptime_;
};
class ClipboardRecentContentIOSTest : public ::testing::Test { class ClipboardRecentContentIOSTest : public ::testing::Test {
protected: protected:
ClipboardRecentContentIOSTest() { ClipboardRecentContentIOSTest() {
...@@ -59,8 +76,9 @@ class ClipboardRecentContentIOSTest : public ::testing::Test { ...@@ -59,8 +76,9 @@ class ClipboardRecentContentIOSTest : public ::testing::Test {
void ResetClipboardRecentContent(const std::string& application_scheme, void ResetClipboardRecentContent(const std::string& application_scheme,
base::TimeDelta time_delta) { base::TimeDelta time_delta) {
clipboard_content_.reset( clipboard_content_.reset(new ClipboardRecentContentIOSWithFakeUptime(
new ClipboardRecentContentIOS(application_scheme, time_delta)); application_scheme, [NSUserDefaults standardUserDefaults]));
clipboard_content_->SetUptime(time_delta);
} }
void SetStoredPasteboardChangeDate(NSDate* changeDate) { void SetStoredPasteboardChangeDate(NSDate* changeDate) {
...@@ -74,7 +92,7 @@ class ClipboardRecentContentIOSTest : public ::testing::Test { ...@@ -74,7 +92,7 @@ class ClipboardRecentContentIOSTest : public ::testing::Test {
} }
protected: protected:
std::unique_ptr<ClipboardRecentContentIOS> clipboard_content_; std::unique_ptr<ClipboardRecentContentIOSWithFakeUptime> clipboard_content_;
}; };
TEST_F(ClipboardRecentContentIOSTest, SchemeFiltering) { TEST_F(ClipboardRecentContentIOSTest, SchemeFiltering) {
...@@ -153,7 +171,7 @@ TEST_F(ClipboardRecentContentIOSTest, SupressedPasteboard) { ...@@ -153,7 +171,7 @@ TEST_F(ClipboardRecentContentIOSTest, SupressedPasteboard) {
// Check that if the pasteboard changes, the new content is not // Check that if the pasteboard changes, the new content is not
// supressed anymore. // supressed anymore.
SetPasteboardContent(kRecognizedURL); SetPasteboardContent(kRecognizedURL2);
EXPECT_TRUE(clipboard_content_->GetRecentURLFromClipboard(&gurl)); EXPECT_TRUE(clipboard_content_->GetRecentURLFromClipboard(&gurl));
} }
......
...@@ -9,7 +9,7 @@ FakeClipboardRecentContent::FakeClipboardRecentContent() ...@@ -9,7 +9,7 @@ FakeClipboardRecentContent::FakeClipboardRecentContent()
FakeClipboardRecentContent::~FakeClipboardRecentContent() {} FakeClipboardRecentContent::~FakeClipboardRecentContent() {}
bool FakeClipboardRecentContent::GetRecentURLFromClipboard(GURL* url) const { bool FakeClipboardRecentContent::GetRecentURLFromClipboard(GURL* url) {
if (suppress_content_) if (suppress_content_)
return false; return false;
...@@ -28,8 +28,6 @@ void FakeClipboardRecentContent::SuppressClipboardContent() { ...@@ -28,8 +28,6 @@ void FakeClipboardRecentContent::SuppressClipboardContent() {
suppress_content_ = true; suppress_content_ = true;
} }
void FakeClipboardRecentContent::RecentURLDisplayed() {}
void FakeClipboardRecentContent::SetClipboardContent( void FakeClipboardRecentContent::SetClipboardContent(
const GURL& url, const GURL& url,
base::TimeDelta content_age) { base::TimeDelta content_age) {
......
...@@ -18,10 +18,9 @@ class FakeClipboardRecentContent : public ClipboardRecentContent { ...@@ -18,10 +18,9 @@ class FakeClipboardRecentContent : public ClipboardRecentContent {
~FakeClipboardRecentContent() override; ~FakeClipboardRecentContent() override;
// ClipboardRecentContent implementation. // ClipboardRecentContent implementation.
bool GetRecentURLFromClipboard(GURL* url) const override; bool GetRecentURLFromClipboard(GURL* url) override;
base::TimeDelta GetClipboardContentAge() const override; base::TimeDelta GetClipboardContentAge() const override;
void SuppressClipboardContent() override; void SuppressClipboardContent() override;
void RecentURLDisplayed() override;
// Sets the URL and clipboard content age. // Sets the URL and clipboard content age.
void SetClipboardContent(const GURL& url, base::TimeDelta content_age); void SetClipboardContent(const GURL& url, base::TimeDelta content_age);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment