Commit 5548199b authored by sdefresne's avatar sdefresne Committed by Commit bot

[iOS] Upstream SyncSetupService (and mock)

SyncSetupService allows configuring sync. It handles enabling and disabling it,
as well as choosing datatypes. Most actions are delayed until a commit is done,
to allow the complex sync setup flow on iOS.

BUG=429756

Review URL: https://codereview.chromium.org/1148573002

Cr-Commit-Position: refs/heads/master@{#330736}
parent 2d902560
...@@ -19,10 +19,12 @@ include_rules = [ ...@@ -19,10 +19,12 @@ include_rules = [
"+components/variations", "+components/variations",
"+components/web_resource", "+components/web_resource",
"+components/webp_transcode", "+components/webp_transcode",
"+google_apis",
"+ios/net", "+ios/net",
"+ios/public/provider/chrome", "+ios/public/provider/chrome",
"+ios/web/public", "+ios/web/public",
"+net", "+net",
"+sync",
"+third_party/google_toolbox_for_mac", "+third_party/google_toolbox_for_mac",
"+ui", "+ui",
......
// Copyright 2012 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 "ios/chrome/browser/sync/sync_setup_service.h"
#include <stdio.h>
#include "base/prefs/pref_service.h"
#include "components/sync_driver/sync_prefs.h"
#include "components/sync_driver/sync_service.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "ios/chrome/browser/pref_names.h"
#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
#include "net/base/network_change_notifier.h"
#include "sync/protocol/sync_protocol_error.h"
namespace {
// The set of user-selectable datatypes. This must be in the same order as
// |SyncSetupService::SyncableDatatype|.
syncer::ModelType kDataTypes[] = {
syncer::BOOKMARKS,
syncer::TYPED_URLS,
syncer::PASSWORDS,
syncer::PROXY_TABS,
syncer::AUTOFILL,
};
} // namespace
SyncSetupService::SyncSetupService(sync_driver::SyncService* sync_service,
PrefService* prefs)
: sync_service_(sync_service), prefs_(prefs) {
DCHECK(sync_service_);
DCHECK(prefs_);
for (unsigned int i = 0; i < arraysize(kDataTypes); ++i) {
user_selectable_types_.Put(kDataTypes[i]);
}
}
SyncSetupService::~SyncSetupService() {
}
syncer::ModelType SyncSetupService::GetModelType(SyncableDatatype datatype) {
DCHECK(datatype < arraysize(kDataTypes));
return kDataTypes[datatype];
}
syncer::ModelTypeSet SyncSetupService::GetDataTypes() const {
return sync_service_->GetPreferredDataTypes();
}
bool SyncSetupService::IsDataTypeEnabled(syncer::ModelType datatype) const {
return GetDataTypes().Has(datatype);
}
void SyncSetupService::SetDataTypeEnabled(syncer::ModelType datatype,
bool enabled) {
sync_service_->SetSetupInProgress(true);
syncer::ModelTypeSet types = GetDataTypes();
if (enabled)
types.Put(datatype);
else
types.Remove(datatype);
types.RetainAll(user_selectable_types_);
if (enabled && !IsSyncEnabled())
SetSyncEnabledWithoutChangingDatatypes(true);
sync_service_->OnUserChoseDatatypes(IsSyncingAllDataTypes(), types);
if (GetDataTypes().Empty())
SetSyncEnabled(false);
}
bool SyncSetupService::UserActionIsRequiredToHaveSyncWork() {
if (!IsSyncEnabled() || !IsDataTypeEnabled(syncer::PROXY_TABS)) {
return true;
}
switch (this->GetSyncServiceState()) {
// No error.
case SyncSetupService::kNoSyncServiceError:
// These errors are transient and don't mean that sync is off.
case SyncSetupService::kSyncServiceCouldNotConnect:
case SyncSetupService::kSyncServiceServiceUnavailable:
return false;
// These errors effectively amount to disabled sync and require a signin.
case SyncSetupService::kSyncServiceSignInNeedsUpdate:
case SyncSetupService::kSyncServiceNeedsPassphrase:
case SyncSetupService::kSyncServiceUnrecoverableError:
return true;
default:
NOTREACHED() << "Unknown sync service state.";
return true;
}
}
bool SyncSetupService::IsSyncingAllDataTypes() const {
sync_driver::SyncPrefs sync_prefs(prefs_);
return sync_prefs.HasKeepEverythingSynced();
}
void SyncSetupService::SetSyncingAllDataTypes(bool sync_all) {
sync_service_->SetSetupInProgress(true);
if (sync_all && !IsSyncEnabled())
SetSyncEnabled(true);
sync_service_->OnUserChoseDatatypes(sync_all, GetDataTypes());
}
bool SyncSetupService::IsSyncEnabled() const {
return sync_service_->IsSyncEnabledAndLoggedIn();
}
void SyncSetupService::SetSyncEnabled(bool sync_enabled) {
SetSyncEnabledWithoutChangingDatatypes(sync_enabled);
if (sync_enabled && GetDataTypes().Empty())
SetSyncingAllDataTypes(true);
}
SyncSetupService::SyncServiceState SyncSetupService::GetSyncServiceState() {
switch (sync_service_->GetAuthError().state()) {
case GoogleServiceAuthError::REQUEST_CANCELED:
return kSyncServiceCouldNotConnect;
// Based on sync_ui_util::GetStatusLabelsForAuthError, SERVICE_UNAVAILABLE
// corresponds to sync having been disabled for the user's domain.
case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
return kSyncServiceServiceUnavailable;
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
return kSyncServiceSignInNeedsUpdate;
// The following errors are not shown to the user.
case GoogleServiceAuthError::NONE:
// Connection failed is not shown to the user, as this will happen if the
// service retuned a 500 error. A more detail error can always be checked
// on about:sync.
case GoogleServiceAuthError::CONNECTION_FAILED:
case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
break;
// The following errors are unexpected on iOS.
case GoogleServiceAuthError::CAPTCHA_REQUIRED:
case GoogleServiceAuthError::ACCOUNT_DELETED:
case GoogleServiceAuthError::ACCOUNT_DISABLED:
case GoogleServiceAuthError::TWO_FACTOR:
case GoogleServiceAuthError::HOSTED_NOT_ALLOWED:
case GoogleServiceAuthError::SERVICE_ERROR:
case GoogleServiceAuthError::WEB_LOGIN_REQUIRED:
// Conventional value for counting the states, never used.
case GoogleServiceAuthError::NUM_STATES:
NOTREACHED() << "Unexpected Auth error ("
<< sync_service_->GetAuthError().state()
<< "): " << sync_service_->GetAuthError().error_message();
break;
}
if (sync_service_->HasUnrecoverableError())
return kSyncServiceUnrecoverableError;
if (sync_service_->IsPassphraseRequiredForDecryption())
return kSyncServiceNeedsPassphrase;
return kNoSyncServiceError;
}
bool SyncSetupService::HasFinishedInitialSetup() {
// Sync initial setup is considered to finished iff:
// 1. User is signed in with sync enabled and the sync setup was completed.
// OR
// 2. User is not signed in or has disabled sync.
return !sync_service_->IsSyncEnabledAndLoggedIn() ||
sync_service_->HasSyncSetupCompleted();
}
void SyncSetupService::PrepareForFirstSyncSetup() {
// |PrepareForFirstSyncSetup| should always be called while the user is signed
// out. At that time, sync setup is not completed.
DCHECK(!sync_service_->HasSyncSetupCompleted());
sync_service_->SetSetupInProgress(true);
}
void SyncSetupService::CommitChanges() {
if (sync_service_->FirstSetupInProgress()) {
// Turn on the sync setup completed flag only if the user did not turn sync
// off.
if (sync_service_->IsSyncEnabledAndLoggedIn()) {
sync_service_->SetSyncSetupCompleted();
}
}
sync_service_->SetSetupInProgress(false);
}
bool SyncSetupService::HasUncommittedChanges() {
return sync_service_->setup_in_progress();
}
void SyncSetupService::SetSyncEnabledWithoutChangingDatatypes(
bool sync_enabled) {
sync_service_->SetSetupInProgress(true);
if (sync_enabled)
sync_service_->UnsuppressAndStart();
else
sync_service_->StopAndSuppress();
}
// Copyright 2012 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 IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_H_
#define IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_H_
#include <map>
#include "base/basictypes.h"
#include "base/strings/string16.h"
#include "components/keyed_service/core/keyed_service.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/util/syncer_error.h"
namespace sync_driver {
class SyncService;
}
class PrefService;
// Class that allows configuring sync. It handles enabling and disabling it, as
// well as choosing datatypes. Most actions are delayed until a commit is done,
// to allow the complex sync setup flow on iOS.
class SyncSetupService : public KeyedService {
public:
typedef enum {
kNoSyncServiceError,
kSyncServiceSignInNeedsUpdate,
kSyncServiceCouldNotConnect,
kSyncServiceServiceUnavailable,
kSyncServiceNeedsPassphrase,
kSyncServiceUnrecoverableError,
kLastSyncServiceError = kSyncServiceUnrecoverableError
} SyncServiceState;
// The set of user-selectable datatypes handled by Chrome for iOS.
typedef enum {
kSyncBookmarks,
kSyncOmniboxHistory,
kSyncPasswords,
kSyncOpenTabs,
kSyncAutofill,
kNumberOfSyncableDatatypes
} SyncableDatatype;
SyncSetupService(sync_driver::SyncService* sync_service, PrefService* prefs);
~SyncSetupService() override;
// Returns the |syncer::ModelType| associated to the given
// |SyncableDatatypes|.
syncer::ModelType GetModelType(SyncableDatatype datatype);
// Returns whether sync is enabled.
virtual bool IsSyncEnabled() const;
// Enables or disables sync. Changes won't take effect in the sync backend
// before the next call to |CommitChanges|.
virtual void SetSyncEnabled(bool sync_enabled);
// Returns all currently enabled datatypes.
syncer::ModelTypeSet GetDataTypes() const;
// Returns whether the given datatype is enabled.
virtual bool IsDataTypeEnabled(syncer::ModelType datatype) const;
// Enables or disables the given datatype. To be noted: this can be called at
// any time, but will only be meaningful if |IsSyncEnabled| is true and
// |IsSyncingAllDataTypes| is false. Changes won't take effect in the sync
// backend before the next call to |CommitChanges|.
void SetDataTypeEnabled(syncer::ModelType datatype, bool enabled);
// Returns whether the user needs to enter a passphrase or enable sync to make
// sync work.
bool UserActionIsRequiredToHaveSyncWork();
// Returns whether all datatypes are being synced.
virtual bool IsSyncingAllDataTypes() const;
// Sets whether all datatypes should be synced or not. Changes won't take
// effect before the next call to |CommitChanges|.
virtual void SetSyncingAllDataTypes(bool sync_all);
// Returns the current sync service state.
virtual SyncServiceState GetSyncServiceState();
// Returns true if the user has gone through the initial sync configuration.
// This method is guaranteed not to start the sync backend so it can be
// called at start-up.
bool HasFinishedInitialSetup();
// Pauses sync allowing the user to configure what data to sync before
// actually starting to sync data with the server.
void PrepareForFirstSyncSetup();
// Commit the current state of the configuration to the sync backend.
void CommitChanges();
// Returns true if there are uncommitted sync changes;
bool HasUncommittedChanges();
private:
// Enables or disables sync. Changes won't take effect in the sync backend
// before the next call to |CommitChanges|. No changes are made to the
// currently selected datatypes.
void SetSyncEnabledWithoutChangingDatatypes(bool sync_enabled);
sync_driver::SyncService* const sync_service_;
PrefService* const prefs_;
syncer::ModelTypeSet user_selectable_types_;
DISALLOW_COPY_AND_ASSIGN(SyncSetupService);
};
#endif // IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_H_
// Copyright 2013 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 "ios/chrome/browser/sync/sync_setup_service_mock.h"
SyncSetupServiceMock::SyncSetupServiceMock(
sync_driver::SyncService* sync_service,
PrefService* prefs)
: SyncSetupService(sync_service, prefs) {
}
SyncSetupServiceMock::~SyncSetupServiceMock() {
}
// Copyright 2013 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 IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_MOCK_H_
#define IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_MOCK_H_
#include "ios/chrome/browser/sync/sync_setup_service.h"
#include "testing/gmock/include/gmock/gmock.h"
// Mock for the class that allows configuring sync on iOS.
class SyncSetupServiceMock : public SyncSetupService {
public:
SyncSetupServiceMock(sync_driver::SyncService* sync_service,
PrefService* prefs);
~SyncSetupServiceMock();
MOCK_CONST_METHOD0(IsSyncEnabled, bool());
MOCK_CONST_METHOD0(IsSyncingAllDataTypes, bool());
MOCK_METHOD0(GetSyncServiceState, SyncServiceState());
MOCK_CONST_METHOD1(IsDataTypeEnabled, bool(syncer::ModelType));
};
#endif // IOS_CHROME_BROWSER_SYNC_SYNC_SETUP_SERVICE_MOCK_H_
...@@ -61,8 +61,10 @@ ...@@ -61,8 +61,10 @@
'../../components/components.gyp:web_resource', '../../components/components.gyp:web_resource',
'../../components/components.gyp:webp_transcode', '../../components/components.gyp:webp_transcode',
'../../components/components_strings.gyp:components_strings', '../../components/components_strings.gyp:components_strings',
'../../google_apis/google_apis.gyp:google_apis',
'../../net/net.gyp:net', '../../net/net.gyp:net',
'../../skia/skia.gyp:skia', '../../skia/skia.gyp:skia',
'../../sync/sync.gyp:sync',
'../../third_party/google_toolbox_for_mac/google_toolbox_for_mac.gyp:google_toolbox_for_mac', '../../third_party/google_toolbox_for_mac/google_toolbox_for_mac.gyp:google_toolbox_for_mac',
'../../ui/base/ui_base.gyp:ui_base', '../../ui/base/ui_base.gyp:ui_base',
'../../ui/gfx/gfx.gyp:gfx', '../../ui/gfx/gfx.gyp:gfx',
...@@ -208,6 +210,8 @@ ...@@ -208,6 +210,8 @@
'browser/suggestions/suggestions_service_factory.mm', 'browser/suggestions/suggestions_service_factory.mm',
'browser/sync/sync_observer_bridge.h', 'browser/sync/sync_observer_bridge.h',
'browser/sync/sync_observer_bridge.mm', 'browser/sync/sync_observer_bridge.mm',
'browser/sync/sync_setup_service.cc',
'browser/sync/sync_setup_service.h',
'browser/translate/after_translate_infobar_controller.h', 'browser/translate/after_translate_infobar_controller.h',
'browser/translate/after_translate_infobar_controller.mm', 'browser/translate/after_translate_infobar_controller.mm',
'browser/translate/before_translate_infobar_controller.h', 'browser/translate/before_translate_infobar_controller.h',
......
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
'browser/geolocation/test_location_manager.mm', 'browser/geolocation/test_location_manager.mm',
'browser/net/mock_image_fetcher.h', 'browser/net/mock_image_fetcher.h',
'browser/net/mock_image_fetcher.mm', 'browser/net/mock_image_fetcher.mm',
'browser/sync/sync_setup_service_mock.cc',
'browser/sync/sync_setup_service_mock.h',
'test/ios_chrome_unit_test_suite.cc', 'test/ios_chrome_unit_test_suite.cc',
'test/ios_chrome_unit_test_suite.h', 'test/ios_chrome_unit_test_suite.h',
'test/run_all_unittests.cc', 'test/run_all_unittests.cc',
......
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