Commit 9c86de87 authored by John Z Wu's avatar John Z Wu Committed by Commit Bot

Refactor CWVSyncController for butter sync

- Replace SigninErrorController with the errors wrapped by SyncService
- Call opt ins for transport only data
- Rely on SyncService state change for delegate callbacks
- Introduce some new userInfo keys in the NSError object
- Redo unit test to use more fakes

Change-Id: Ibb954e142c254627380658dc9af1e624b96edf98
Bug: 1056416
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2103555
Commit-Queue: John Wu <jzw@chromium.org>
Reviewed-by: default avatarHiroshi Ichikawa <ichikawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759132}
parent 4ee74a1c
...@@ -188,8 +188,6 @@ source_set("web_view_sources") { ...@@ -188,8 +188,6 @@ source_set("web_view_sources") {
"internal/signin/web_view_identity_manager_factory.mm", "internal/signin/web_view_identity_manager_factory.mm",
"internal/signin/web_view_signin_client_factory.h", "internal/signin/web_view_signin_client_factory.h",
"internal/signin/web_view_signin_client_factory.mm", "internal/signin/web_view_signin_client_factory.mm",
"internal/signin/web_view_signin_error_controller_factory.h",
"internal/signin/web_view_signin_error_controller_factory.mm",
"internal/sync/cwv_sync_controller.mm", "internal/sync/cwv_sync_controller.mm",
"internal/sync/cwv_sync_controller_internal.h", "internal/sync/cwv_sync_controller_internal.h",
"internal/sync/web_view_device_info_sync_service_factory.h", "internal/sync/web_view_device_info_sync_service_factory.h",
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#import "ios/web_view/internal/cwv_web_view_internal.h" #import "ios/web_view/internal/cwv_web_view_internal.h"
#import "ios/web_view/internal/passwords/web_view_account_password_store_factory.h" #import "ios/web_view/internal/passwords/web_view_account_password_store_factory.h"
#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h" #include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
#include "ios/web_view/internal/signin/web_view_signin_error_controller_factory.h"
#import "ios/web_view/internal/sync/cwv_sync_controller_internal.h" #import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
#import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h" #import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h"
#include "ios/web_view/internal/web_view_browser_state.h" #include "ios/web_view/internal/web_view_browser_state.h"
...@@ -147,14 +146,10 @@ CWVWebViewConfiguration* gIncognitoConfiguration = nil; ...@@ -147,14 +146,10 @@ CWVWebViewConfiguration* gIncognitoConfiguration = nil;
signin::IdentityManager* identityManager = signin::IdentityManager* identityManager =
ios_web_view::WebViewIdentityManagerFactory::GetForBrowserState( ios_web_view::WebViewIdentityManagerFactory::GetForBrowserState(
self.browserState); self.browserState);
SigninErrorController* signinErrorController = _syncController = [[CWVSyncController alloc]
ios_web_view::WebViewSigninErrorControllerFactory::GetForBrowserState( initWithSyncService:syncService
self.browserState);
_syncController =
[[CWVSyncController alloc] initWithSyncService:syncService
identityManager:identityManager identityManager:identityManager
signinErrorController:signinErrorController]; prefService:_browserState->GetPrefs()];
} }
return _syncController; return _syncController;
} }
...@@ -179,7 +174,6 @@ CWVWebViewConfiguration* gIncognitoConfiguration = nil; ...@@ -179,7 +174,6 @@ CWVWebViewConfiguration* gIncognitoConfiguration = nil;
for (CWVWebView* webView in _webViews) { for (CWVWebView* webView in _webViews) {
[webView shutDown]; [webView shutDown];
} }
[_syncController shutDown];
_browserState.reset(); _browserState.reset();
} }
......
// Copyright 2017 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_WEB_VIEW_INTERNAL_SIGNIN_WEB_VIEW_SIGNIN_ERROR_CONTROLLER_FACTORY_H_
#define IOS_WEB_VIEW_INTERNAL_SIGNIN_WEB_VIEW_SIGNIN_ERROR_CONTROLLER_FACTORY_H_
#include <memory>
#include "base/macros.h"
#include "base/no_destructor.h"
#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
class SigninErrorController;
namespace ios_web_view {
class WebViewBrowserState;
// Singleton that owns all SigninErrorControllers and associates them with
// a browser state.
class WebViewSigninErrorControllerFactory
: public BrowserStateKeyedServiceFactory {
public:
static SigninErrorController* GetForBrowserState(
ios_web_view::WebViewBrowserState* browser_state);
static WebViewSigninErrorControllerFactory* GetInstance();
private:
friend class base::NoDestructor<WebViewSigninErrorControllerFactory>;
WebViewSigninErrorControllerFactory();
~WebViewSigninErrorControllerFactory() override = default;
// BrowserStateKeyedServiceFactory implementation.
std::unique_ptr<KeyedService> BuildServiceInstanceFor(
web::BrowserState* context) const override;
DISALLOW_COPY_AND_ASSIGN(WebViewSigninErrorControllerFactory);
};
} // namespace ios_web_view
#endif // IOS_WEB_VIEW_INTERNAL_SIGNIN_WEB_VIEW_SIGNIN_ERROR_CONTROLLER_FACTORY_H_
// Copyright 2017 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/web_view/internal/signin/web_view_signin_error_controller_factory.h"
#include <utility>
#include "base/no_destructor.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/keyed_service/ios/browser_state_dependency_manager.h"
#include "components/signin/core/browser/signin_error_controller.h"
#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
#include "ios/web_view/internal/web_view_browser_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ios_web_view {
// static
SigninErrorController* WebViewSigninErrorControllerFactory::GetForBrowserState(
ios_web_view::WebViewBrowserState* browser_state) {
return static_cast<SigninErrorController*>(
GetInstance()->GetServiceForBrowserState(browser_state, true));
}
// static
WebViewSigninErrorControllerFactory*
WebViewSigninErrorControllerFactory::GetInstance() {
static base::NoDestructor<WebViewSigninErrorControllerFactory> instance;
return instance.get();
}
WebViewSigninErrorControllerFactory::WebViewSigninErrorControllerFactory()
: BrowserStateKeyedServiceFactory(
"SigninErrorController",
BrowserStateDependencyManager::GetInstance()) {
DependsOn(WebViewIdentityManagerFactory::GetInstance());
}
std::unique_ptr<KeyedService>
WebViewSigninErrorControllerFactory::BuildServiceInstanceFor(
web::BrowserState* context) const {
WebViewBrowserState* browser_state =
WebViewBrowserState::FromBrowserState(context);
return std::make_unique<SigninErrorController>(
SigninErrorController::AccountMode::ANY_ACCOUNT,
WebViewIdentityManagerFactory::GetForBrowserState(browser_state));
}
} // namespace ios_web_view
...@@ -7,19 +7,15 @@ ...@@ -7,19 +7,15 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#include <memory> #include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/time/time.h" #include "components/autofill/core/common/autofill_prefs.h"
#include "components/signin/core/browser/signin_error_controller.h" #include "components/password_manager/core/browser/password_manager_util.h"
#include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/device_accounts_synchronizer.h" #include "components/signin/public/identity_manager/device_accounts_synchronizer.h"
#include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h" #include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/sync/driver/sync_service.h" #include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_user_settings.h" #include "components/sync/driver/sync_user_settings.h"
#include "ios/web/public/thread/web_thread.h"
#import "ios/web_view/public/cwv_identity.h" #import "ios/web_view/public/cwv_identity.h"
#import "ios/web_view/public/cwv_sync_controller_data_source.h" #import "ios/web_view/public/cwv_sync_controller_data_source.h"
#import "ios/web_view/public/cwv_sync_controller_delegate.h" #import "ios/web_view/public/cwv_sync_controller_delegate.h"
...@@ -30,6 +26,12 @@ ...@@ -30,6 +26,12 @@
NSErrorDomain const CWVSyncErrorDomain = NSErrorDomain const CWVSyncErrorDomain =
@"org.chromium.chromewebview.SyncErrorDomain"; @"org.chromium.chromewebview.SyncErrorDomain";
NSErrorUserInfoKey const CWVSyncErrorDescriptionKey =
@"org.chromium.chromewebview.SyncErrorDescriptionKey";
NSErrorUserInfoKey const CWVSyncErrorMessageKey =
@"org.chromium.chromewebview.SyncErrorMessageKey";
NSErrorUserInfoKey const CWVSyncErrorIsTransientKey =
@"org.chromium.chromewebview.SyncErrorIsTransientKey";
namespace { namespace {
CWVSyncError CWVConvertGoogleServiceAuthErrorStateToCWVSyncError( CWVSyncError CWVConvertGoogleServiceAuthErrorStateToCWVSyncError(
...@@ -60,18 +62,13 @@ CWVSyncError CWVConvertGoogleServiceAuthErrorStateToCWVSyncError( ...@@ -60,18 +62,13 @@ CWVSyncError CWVConvertGoogleServiceAuthErrorStateToCWVSyncError(
@interface CWVSyncController () @interface CWVSyncController ()
// Called by WebViewSyncControllerObserverBridge's
// |OnSyncConfigurationCompleted|.
- (void)didCompleteSyncConfiguration;
// Called by WebViewSyncControllerObserverBridge's |OnSyncShutdown|. // Called by WebViewSyncControllerObserverBridge's |OnSyncShutdown|.
- (void)didShutdownSync; - (void)didShutdownSync;
// Called by WebViewSyncControllerObserverBridge's |OnErrorChanged|. // Called by WebViewSyncControllerObserverBridge's |OnStateChanged|.
- (void)didUpdateAuthError; - (void)syncStateDidChange;
// Called by WebViewSyncControllerObserverBridge's |OnPrimaryAccountCleared|.
- (void)didClearPrimaryAccount;
// Call to refresh access tokens for |currentIdentity|. // Call to reload accounts from the |dataSource|.
- (void)reloadCredentials; - (void)reloadAccounts;
@end @end
...@@ -79,33 +76,20 @@ namespace ios_web_view { ...@@ -79,33 +76,20 @@ namespace ios_web_view {
// Bridge that observes syncer::SyncService and calls analagous // Bridge that observes syncer::SyncService and calls analagous
// methods on CWVSyncController. // methods on CWVSyncController.
class WebViewSyncControllerObserverBridge class WebViewSyncControllerObserverBridge : public syncer::SyncServiceObserver {
: public syncer::SyncServiceObserver,
public signin::IdentityManager::Observer,
public SigninErrorController::Observer {
public: public:
explicit WebViewSyncControllerObserverBridge( explicit WebViewSyncControllerObserverBridge(
CWVSyncController* sync_controller) CWVSyncController* sync_controller)
: sync_controller_(sync_controller) {} : sync_controller_(sync_controller) {}
// syncer::SyncServiceObserver: void OnStateChanged(syncer::SyncService* sync) override {
void OnSyncConfigurationCompleted(syncer::SyncService* sync) override { [sync_controller_ syncStateDidChange];
[sync_controller_ didCompleteSyncConfiguration];
} }
void OnSyncShutdown(syncer::SyncService* sync) override { void OnSyncShutdown(syncer::SyncService* sync) override {
[sync_controller_ didShutdownSync]; [sync_controller_ didShutdownSync];
} }
// signin::IdentityManager::Observer
void OnPrimaryAccountCleared(
const CoreAccountInfo& previous_primary_account_info) override {
[sync_controller_ didClearPrimaryAccount];
}
// SigninErrorController::Observer:
void OnErrorChanged() override { [sync_controller_ didUpdateAuthError]; }
private: private:
__weak CWVSyncController* sync_controller_; __weak CWVSyncController* sync_controller_;
}; };
...@@ -115,13 +99,12 @@ class WebViewSyncControllerObserverBridge ...@@ -115,13 +99,12 @@ class WebViewSyncControllerObserverBridge
@implementation CWVSyncController { @implementation CWVSyncController {
syncer::SyncService* _syncService; syncer::SyncService* _syncService;
signin::IdentityManager* _identityManager; signin::IdentityManager* _identityManager;
SigninErrorController* _signinErrorController;
std::unique_ptr<ios_web_view::WebViewSyncControllerObserverBridge> _observer; std::unique_ptr<ios_web_view::WebViewSyncControllerObserverBridge> _observer;
PrefService* _prefService;
syncer::SyncService::TransportState _lastTransportState;
GoogleServiceAuthError _lastAuthError;
} }
@synthesize currentIdentity = _currentIdentity;
@synthesize delegate = _delegate;
namespace { namespace {
// Data source that can provide access tokens. // Data source that can provide access tokens.
__weak id<CWVSyncControllerDataSource> gSyncDataSource; __weak id<CWVSyncControllerDataSource> gSyncDataSource;
...@@ -137,32 +120,21 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource; ...@@ -137,32 +120,21 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource;
- (instancetype)initWithSyncService:(syncer::SyncService*)syncService - (instancetype)initWithSyncService:(syncer::SyncService*)syncService
identityManager:(signin::IdentityManager*)identityManager identityManager:(signin::IdentityManager*)identityManager
signinErrorController: prefService:(PrefService*)prefService {
(SigninErrorController*)signinErrorController {
self = [super init]; self = [super init];
if (self) { if (self) {
_syncService = syncService; _syncService = syncService;
_identityManager = identityManager; _identityManager = identityManager;
_signinErrorController = signinErrorController; _prefService = prefService;
_observer = _observer =
std::make_unique<ios_web_view::WebViewSyncControllerObserverBridge>( std::make_unique<ios_web_view::WebViewSyncControllerObserverBridge>(
self); self);
_syncService->AddObserver(_observer.get()); _syncService->AddObserver(_observer.get());
_identityManager->AddObserver(_observer.get());
_signinErrorController->AddObserver(_observer.get());
if (_identityManager->HasPrimaryAccount()) {
CoreAccountInfo accountInfo = _identityManager->GetPrimaryAccountInfo();
_currentIdentity = [[CWVIdentity alloc]
initWithEmail:base::SysUTF8ToNSString(accountInfo.email)
fullName:nil
gaiaID:base::SysUTF8ToNSString(accountInfo.gaia)];
}
// Refresh access tokens on foreground to extend expiration dates. // Refresh access tokens on foreground to extend expiration dates.
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
addObserver:self addObserver:self
selector:@selector(reloadCredentials) selector:@selector(reloadAccounts)
name:UIApplicationWillEnterForegroundNotification name:UIApplicationWillEnterForegroundNotification
object:nil]; object:nil];
} }
...@@ -171,23 +143,31 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource; ...@@ -171,23 +143,31 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource;
- (void)dealloc { - (void)dealloc {
_syncService->RemoveObserver(_observer.get()); _syncService->RemoveObserver(_observer.get());
_identityManager->RemoveObserver(_observer.get());
_signinErrorController->RemoveObserver(_observer.get());
} }
#pragma mark - Public Methods #pragma mark - Public Methods
- (CWVIdentity*)currentIdentity {
if (_identityManager->HasPrimaryAccount()) {
CoreAccountInfo accountInfo = _identityManager->GetPrimaryAccountInfo();
return [[CWVIdentity alloc]
initWithEmail:base::SysUTF8ToNSString(accountInfo.email)
fullName:nil
gaiaID:base::SysUTF8ToNSString(accountInfo.gaia)];
}
return nil;
}
- (BOOL)isPassphraseNeeded { - (BOOL)isPassphraseNeeded {
return _syncService->GetUserSettings() return _syncService->GetUserSettings()
->IsPassphraseRequiredForPreferredDataTypes(); ->IsPassphraseRequiredForPreferredDataTypes();
} }
- (void)startSyncWithIdentity:(CWVIdentity*)identity { - (void)startSyncWithIdentity:(CWVIdentity*)identity {
DCHECK(!_currentIdentity) DCHECK(!self.currentIdentity)
<< "Already syncing! Call -stopSyncAndClearIdentity first."; << "Already syncing! Call -stopSyncAndClearIdentity first.";
_currentIdentity = identity;
const CoreAccountId accountId = _identityManager->PickAccountIdForAccount( const CoreAccountId accountId = _identityManager->PickAccountIdForAccount(
base::SysNSStringToUTF8(identity.gaiaID), base::SysNSStringToUTF8(identity.gaiaID),
base::SysNSStringToUTF8(identity.email)); base::SysNSStringToUTF8(identity.email));
...@@ -198,6 +178,13 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource; ...@@ -198,6 +178,13 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource;
_identityManager->GetPrimaryAccountMutator()->SetPrimaryAccount(accountId); _identityManager->GetPrimaryAccountMutator()->SetPrimaryAccount(accountId);
CHECK_EQ(_identityManager->GetPrimaryAccountId(), accountId); CHECK_EQ(_identityManager->GetPrimaryAccountId(), accountId);
autofill::prefs::SetUserOptedInWalletSyncTransport(_prefService, accountId,
/*opted_in=*/true);
password_manager_util::SetDefaultPasswordStore(
_prefService, _syncService, autofill::PasswordForm::Store::kAccountStore);
password_manager_util::SetAccountStorageOptIn(_prefService, _syncService,
/*opt_in=*/true);
} }
- (void)stopSyncAndClearIdentity { - (void)stopSyncAndClearIdentity {
...@@ -215,58 +202,57 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource; ...@@ -215,58 +202,57 @@ __weak id<CWVSyncControllerDataSource> gSyncDataSource;
#pragma mark - Private Methods #pragma mark - Private Methods
- (void)didCompleteSyncConfiguration {
if ([_delegate respondsToSelector:@selector(syncControllerDidStartSync:)]) {
[_delegate syncControllerDidStartSync:self];
}
}
- (void)didShutdownSync { - (void)didShutdownSync {
_syncService->RemoveObserver(_observer.get()); _syncService->RemoveObserver(_observer.get());
_signinErrorController->RemoveObserver(_observer.get());
}
- (void)reloadCredentials {
_identityManager->GetDeviceAccountsSynchronizer()
->ReloadAllAccountsFromSystem();
} }
#pragma mark - Internal Methods - (void)syncStateDidChange {
if (_lastTransportState != _syncService->GetTransportState()) {
_lastTransportState = _syncService->GetTransportState();
- (void)shutDown { if (_lastTransportState == syncer::SyncService::TransportState::ACTIVE) {
_syncService->RemoveObserver(_observer.get()); if ([_delegate
_identityManager->RemoveObserver(_observer.get()); respondsToSelector:@selector(syncControllerDidStartSync:)]) {
_signinErrorController->RemoveObserver(_observer.get()); [_delegate syncControllerDidStartSync:self];
} }
} else if (_lastTransportState ==
- (void)didClearPrimaryAccount { syncer::SyncService::TransportState::DISABLED) {
_currentIdentity = nil; if ([_delegate
if ([_delegate respondsToSelector:@selector(syncControllerDidStopSync:)]) { respondsToSelector:@selector(syncControllerDidStopSync:)]) {
[_delegate syncControllerDidStopSync:self]; [_delegate syncControllerDidStopSync:self];
} }
} }
}
if (_lastAuthError.state() != _syncService->GetAuthError().state()) {
_lastAuthError = _syncService->GetAuthError();
- (void)didUpdateAuthError { CWVSyncError code = CWVConvertGoogleServiceAuthErrorStateToCWVSyncError(
GoogleServiceAuthError authError = _signinErrorController->auth_error(); _lastAuthError.state());
CWVSyncError code = if (code != CWVSyncErrorNone &&
CWVConvertGoogleServiceAuthErrorStateToCWVSyncError(authError.state()); [_delegate respondsToSelector:@selector(syncController:
if (code != CWVSyncErrorNone) { didFailWithError:)]) {
NSString* description =
base::SysUTF8ToNSString(_lastAuthError.ToString());
NSString* message =
base::SysUTF8ToNSString(_lastAuthError.error_message());
BOOL isTransient = _lastAuthError.IsTransientError();
NSError* error = NSError* error =
[NSError errorWithDomain:CWVSyncErrorDomain [NSError errorWithDomain:CWVSyncErrorDomain
code:code code:code
userInfo:@{ userInfo:@{
NSLocalizedDescriptionKey : CWVSyncErrorDescriptionKey : description,
base::SysUTF8ToNSString(authError.ToString()) CWVSyncErrorMessageKey : message,
CWVSyncErrorIsTransientKey : @(isTransient),
}]; }];
[self invokeDelegateDidFailWithError:error]; [_delegate syncController:self didFailWithError:error];
}
} }
} }
- (void)invokeDelegateDidFailWithError:(NSError*)error { - (void)reloadAccounts {
if ([_delegate respondsToSelector:@selector(syncController: _identityManager->GetDeviceAccountsSynchronizer()
didFailWithError:)]) { ->ReloadAllAccountsFromSystem();
[_delegate syncController:self didFailWithError:error];
}
} }
@end @end
...@@ -17,21 +17,16 @@ namespace signin { ...@@ -17,21 +17,16 @@ namespace signin {
class IdentityManager; class IdentityManager;
} // namespace signin } // namespace signin
class SigninErrorController; class PrefService;
@interface CWVSyncController () @interface CWVSyncController ()
// All dependencies must out live this class. // All dependencies must out live this class.
- (instancetype) - (instancetype)initWithSyncService:(syncer::SyncService*)syncService
initWithSyncService:(syncer::SyncService*)syncService
identityManager:(signin::IdentityManager*)identityManager identityManager:(signin::IdentityManager*)identityManager
signinErrorController:(SigninErrorController*)signinErrorController prefService:(PrefService*)prefService
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
// Called by the associated CWVWebViewConfiguration in order to shut
// down cleanly. See CWVWebViewConfiguration's |shutDown| method for more info.
- (void)shutDown;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
......
...@@ -10,34 +10,31 @@ ...@@ -10,34 +10,31 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#import "base/test/ios/wait_util.h" #include "base/test/scoped_feature_list.h"
#include "components/signin/core/browser/signin_error_controller.h" #include "base/test/task_environment.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/image_fetcher/ios/ios_image_decoder_impl.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/public/base/account_consistency_method.h" #include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/signin_pref_names.h" #include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/signin/public/base/test_signin_client.h"
#include "components/signin/public/identity_manager/device_accounts_synchronizer.h"
#include "components/signin/public/identity_manager/identity_manager_builder.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h" #include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/sync/driver/mock_sync_service.h"
#include "components/sync/driver/sync_service_observer.h" #include "components/sync/driver/sync_service_observer.h"
#include "components/sync/driver/test_sync_service.h"
#include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/google_service_auth_error.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/test/scoped_testing_web_client.h"
#include "ios/web/public/test/web_task_environment.h"
#include "ios/web/public/web_client.h"
#include "ios/web_view/internal/app/application_context.h"
#include "ios/web_view/internal/signin/ios_web_view_signin_client.h"
#include "ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h" #include "ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h"
#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
#include "ios/web_view/internal/signin/web_view_signin_client_factory.h"
#include "ios/web_view/internal/signin/web_view_signin_error_controller_factory.h"
#include "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h"
#include "ios/web_view/internal/web_view_browser_state.h"
#import "ios/web_view/public/cwv_identity.h" #import "ios/web_view/public/cwv_identity.h"
#import "ios/web_view/public/cwv_sync_controller_data_source.h" #import "ios/web_view/public/cwv_sync_controller_data_source.h"
#import "ios/web_view/public/cwv_sync_controller_delegate.h" #import "ios/web_view/public/cwv_sync_controller_delegate.h"
#include "ios/web_view/test/test_with_locale_and_resources.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h" #import "testing/gtest_mac.h"
#include "testing/platform_test.h" #include "testing/platform_test.h"
...@@ -50,175 +47,141 @@ ...@@ -50,175 +47,141 @@
namespace ios_web_view { namespace ios_web_view {
namespace { namespace {
using testing::_;
using testing::Invoke;
using testing::Return;
const char kTestGaiaId[] = "1337";
const char kTestEmail[] = "johndoe@chromium.org"; const char kTestEmail[] = "johndoe@chromium.org";
const char kTestFullName[] = "John Doe";
const char kTestPassphrase[] = "dummy-passphrase";
const char kTestScope1[] = "scope1.chromium.org";
const char kTestScope2[] = "scope2.chromium.org";
using base::test::ios::kWaitForActionTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;
std::unique_ptr<KeyedService> BuildMockSyncService(web::BrowserState* context) {
return std::make_unique<syncer::MockSyncService>();
}
} // namespace } // namespace
class CWVSyncControllerTest : public TestWithLocaleAndResources { class CWVSyncControllerTest : public PlatformTest {
protected: protected:
CWVSyncControllerTest() CWVSyncControllerTest() {
: web_client_(std::make_unique<web::WebClient>()), scoped_feature_.InitAndEnableFeature(
browser_state_(/*off_the_record=*/false) { password_manager::features::kEnablePasswordsAccountStorage);
// Clear account info before each test.
PrefService* pref_service = browser_state_.GetPrefs(); pref_service_.registry()->RegisterDictionaryPref(
pref_service->ClearPref(prefs::kGoogleServicesAccountId); autofill::prefs::kAutofillSyncTransportOptIn);
pref_service->ClearPref(prefs::kGoogleServicesConsentedToSync); pref_service_.registry()->RegisterDictionaryPref(
pref_service->ClearPref(prefs::kAccountInfo); password_manager::prefs::kAccountStoragePerAccountSettings);
WebViewProfileSyncServiceFactory::GetInstance()->SetTestingFactory( // Change the default transport state to be disabled.
&browser_state_, base::BindRepeating(&BuildMockSyncService)); sync_service_.SetTransportState(
syncer::SyncService::TransportState::DISABLED);
EXPECT_CALL(*mock_sync_service(), AddObserver(_))
.WillOnce(Invoke(this, &CWVSyncControllerTest::AddObserver));
sync_controller_ = [[CWVSyncController alloc]
initWithSyncService:mock_sync_service()
identityManager:identity_manager()
signinErrorController:signin_error_controller()];
} }
~CWVSyncControllerTest() override { base::test::ScopedFeatureList scoped_feature_;
EXPECT_CALL(*mock_sync_service(), RemoveObserver(_)); base::test::TaskEnvironment task_environment_;
EXPECT_CALL(*mock_sync_service(), Shutdown()); signin::IdentityTestEnvironment identity_test_environment_;
} syncer::TestSyncService sync_service_;
TestingPrefServiceSimple local_state_;
void AddObserver(syncer::SyncServiceObserver* observer) { TestingPrefServiceSimple pref_service_;
sync_service_observer_ = observer;
}
signin::IdentityManager* identity_manager() {
return WebViewIdentityManagerFactory::GetForBrowserState(&browser_state_);
}
syncer::MockSyncService* mock_sync_service() {
return static_cast<syncer::MockSyncService*>(
WebViewProfileSyncServiceFactory::GetForBrowserState(&browser_state_));
}
SigninErrorController* signin_error_controller() {
return WebViewSigninErrorControllerFactory::GetForBrowserState(
&browser_state_);
}
web::WebTaskEnvironment task_environment_;
web::ScopedTestingWebClient web_client_;
ios_web_view::WebViewBrowserState browser_state_;
CWVSyncController* sync_controller_ = nil;
syncer::SyncServiceObserver* sync_service_observer_ = nullptr;
}; };
// Verifies CWVSyncControllerDataSource methods are invoked with the correct TEST_F(CWVSyncControllerTest, StartSyncWithIdentity) {
// parameters. CoreAccountInfo account_info =
TEST_F(CWVSyncControllerTest, DataSourceCallbacks) { identity_test_environment_.MakeAccountAvailable(kTestEmail);
// [data_source expect] returns an autoreleased object, but it must be
// destroyed before this test exits to avoid holding on to |sync_controller_|. CWVIdentity* identity = [[CWVIdentity alloc]
@autoreleasepool { initWithEmail:@(kTestEmail)
id data_source = OCMProtocolMock(@protocol(CWVSyncControllerDataSource)); fullName:nil
CWVSyncController.dataSource = data_source; gaiaID:base::SysUTF8ToNSString(account_info.gaia)];
[[data_source expect]
fetchAccessTokenForIdentity:[OCMArg checkWithBlock:^BOOL( // Preconfigure TestSyncService as if it was enabled in transport mode.
CWVIdentity* identity) { sync_service_.SetFirstSetupComplete(false);
return [identity.gaiaID isEqualToString:@(kTestGaiaId)]; sync_service_.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
}] sync_service_.SetIsUsingSecondaryPassphrase(false);
scopes:[OCMArg checkWithBlock:^BOOL( sync_service_.SetAuthenticatedAccountInfo(account_info);
NSArray* scopes) {
return [scopes containsObject:@(kTestScope1)] && CWVSyncController* sync_controller = [[CWVSyncController alloc]
[scopes containsObject:@(kTestScope2)]; initWithSyncService:&sync_service_
}] identityManager:identity_test_environment_.identity_manager()
completionHandler:[OCMArg any]]; prefService:&pref_service_];
[sync_controller startSyncWithIdentity:identity];
WebViewDeviceAccountsProviderImpl accounts_provider; EXPECT_NSEQ(sync_controller.currentIdentity.gaiaID, identity.gaiaID);
std::set<std::string> scopes = {kTestScope1, kTestScope2};
accounts_provider.GetAccessToken(kTestGaiaId, "dummy-client-id", scopes, CoreAccountInfo primary_account_info =
base::DoNothing()); identity_test_environment_.identity_manager()->GetPrimaryAccountInfo();
EXPECT_EQ(primary_account_info, account_info);
[data_source verify];
} // Ensure opt-ins for transport only sync data is flipped to true.
EXPECT_TRUE(autofill::prefs::IsUserOptedInWalletSyncTransport(
&pref_service_, primary_account_info.account_id));
EXPECT_EQ(password_manager_util::GetDefaultPasswordStore(&pref_service_,
&sync_service_),
autofill::PasswordForm::Store::kAccountStore);
EXPECT_TRUE(password_manager_util::IsOptedInForAccountStorage(
&pref_service_, &sync_service_));
} }
// Verifies CWVSyncControllerDelegate methods are invoked with the correct TEST_F(CWVSyncControllerTest, StopSyncAndClearIdentity) {
// parameters. CoreAccountInfo account_info =
TEST_F(CWVSyncControllerTest, DelegateCallbacks) { identity_test_environment_.MakePrimaryAccountAvailable(kTestEmail);
// [delegate expect] returns an autoreleased object, but it must be destroyed
// before this test exits to avoid holding on to |sync_controller_|. CWVSyncController* sync_controller = [[CWVSyncController alloc]
@autoreleasepool { initWithSyncService:&sync_service_
CWVIdentity* identity = [[CWVIdentity alloc] initWithEmail:@(kTestEmail) identityManager:identity_test_environment_.identity_manager()
fullName:@(kTestFullName) prefService:&pref_service_];
gaiaID:@(kTestGaiaId)]; CWVIdentity* current_identity = sync_controller.currentIdentity;
id data_source = OCMProtocolMock(@protocol(CWVSyncControllerDataSource)); ASSERT_TRUE(current_identity);
[[[data_source stub] andReturn:@[ identity ]] allKnownIdentities]; EXPECT_NSEQ(current_identity.gaiaID,
CWVSyncController.dataSource = data_source; base::SysUTF8ToNSString(account_info.gaia));
id delegate = OCMProtocolMock(@protocol(CWVSyncControllerDelegate)); EXPECT_NSEQ(current_identity.email, base::SysUTF8ToNSString(kTestEmail));
sync_controller_.delegate = delegate;
[sync_controller_ startSyncWithIdentity:identity]; [sync_controller stopSyncAndClearIdentity];
EXPECT_FALSE(sync_controller.currentIdentity);
[[delegate expect] syncControllerDidStartSync:sync_controller_]; }
sync_service_observer_->OnSyncConfigurationCompleted(mock_sync_service());
TEST_F(CWVSyncControllerTest, PassphraseNeeded) {
[[delegate expect] CWVSyncController* sync_controller = [[CWVSyncController alloc]
syncController:sync_controller_ initWithSyncService:&sync_service_
didFailWithError:[OCMArg checkWithBlock:^BOOL(NSError* error) { identityManager:identity_test_environment_.identity_manager()
return error.code == CWVSyncErrorInvalidGAIACredentials; prefService:&pref_service_];
}]]; sync_service_.SetPassphraseRequiredForPreferredDataTypes(false);
GoogleServiceAuthError auth_error( EXPECT_FALSE(sync_controller.passphraseNeeded);
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); sync_service_.SetPassphraseRequiredForPreferredDataTypes(true);
signin::UpdatePersistentErrorOfRefreshTokenForAccount( EXPECT_TRUE(sync_controller.passphraseNeeded);
identity_manager(), identity_manager()->GetPrimaryAccountId(), }
auth_error);
[[delegate expect] syncControllerDidStopSync:sync_controller_]; TEST_F(CWVSyncControllerTest, DelegateDidStartAndStopSync) {
[sync_controller_ stopSyncAndClearIdentity]; CWVSyncController* sync_controller = [[CWVSyncController alloc]
initWithSyncService:&sync_service_
identityManager:identity_test_environment_.identity_manager()
prefService:&pref_service_];
id delegate = OCMStrictProtocolMock(@protocol(CWVSyncControllerDelegate));
sync_controller.delegate = delegate;
// TestSyncService's transport state has to actually change before a callback
// will be fired, so we have to start it before we can stop it.
OCMExpect([delegate syncControllerDidStartSync:sync_controller]);
OCMExpect([delegate syncControllerDidStopSync:sync_controller]);
sync_service_.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
sync_service_.FireStateChanged();
sync_service_.SetTransportState(
syncer::SyncService::TransportState::DISABLED);
sync_service_.FireStateChanged();
[delegate verify]; [delegate verify];
}
} }
// Verifies CWVSyncController properly maintains the current syncing user. TEST_F(CWVSyncControllerTest, DelegateDidFailWithError) {
TEST_F(CWVSyncControllerTest, CurrentIdentity) { CWVSyncController* sync_controller = [[CWVSyncController alloc]
CWVIdentity* identity = [[CWVIdentity alloc] initWithEmail:@(kTestEmail) initWithSyncService:&sync_service_
fullName:@(kTestFullName) identityManager:identity_test_environment_.identity_manager()
gaiaID:@(kTestGaiaId)]; prefService:&pref_service_];
id data_source = OCMProtocolMock(@protocol(CWVSyncControllerDataSource));
[[[data_source stub] andReturn:@[ identity ]] allKnownIdentities]; id delegate = OCMStrictProtocolMock(@protocol(CWVSyncControllerDelegate));
CWVSyncController.dataSource = data_source; sync_controller.delegate = delegate;
[sync_controller_ startSyncWithIdentity:identity];
CWVIdentity* currentIdentity = sync_controller_.currentIdentity;
EXPECT_TRUE(currentIdentity);
EXPECT_NSEQ(identity.email, currentIdentity.email);
EXPECT_NSEQ(identity.fullName, currentIdentity.fullName);
EXPECT_NSEQ(identity.gaiaID, currentIdentity.gaiaID);
[sync_controller_ stopSyncAndClearIdentity];
EXPECT_FALSE(sync_controller_.currentIdentity);
}
// Verifies CWVSyncController's passphrase API. OCMExpect([delegate
TEST_F(CWVSyncControllerTest, Passphrase) { syncController:sync_controller
EXPECT_CALL(*mock_sync_service()->GetMockUserSettings(), didFailWithError:[OCMArg checkWithBlock:^BOOL(NSError* error) {
IsPassphraseRequiredForPreferredDataTypes()) return error.code == CWVSyncErrorConnectionFailed &&
.WillOnce(Return(true)); error.domain == CWVSyncErrorDomain &&
EXPECT_TRUE(sync_controller_.passphraseNeeded); [error.userInfo[CWVSyncErrorIsTransientKey] boolValue];
EXPECT_CALL(*mock_sync_service()->GetMockUserSettings(), }]]);
SetDecryptionPassphrase(kTestPassphrase)) sync_service_.SetAuthError(GoogleServiceAuthError::FromConnectionError(0));
.WillOnce(Return(true)); sync_service_.FireStateChanged();
EXPECT_TRUE([sync_controller_ unlockWithPassphrase:@(kTestPassphrase)]);
[delegate verify];
} }
} // namespace ios_web_view } // namespace ios_web_view
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#include "ios/web_view/internal/passwords/web_view_password_store_factory.h" #include "ios/web_view/internal/passwords/web_view_password_store_factory.h"
#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h" #include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
#include "ios/web_view/internal/signin/web_view_signin_client_factory.h" #include "ios/web_view/internal/signin/web_view_signin_client_factory.h"
#include "ios/web_view/internal/signin/web_view_signin_error_controller_factory.h"
#import "ios/web_view/internal/sync/web_view_gcm_profile_service_factory.h" #import "ios/web_view/internal/sync/web_view_gcm_profile_service_factory.h"
#import "ios/web_view/internal/sync/web_view_model_type_store_service_factory.h" #import "ios/web_view/internal/sync/web_view_model_type_store_service_factory.h"
#import "ios/web_view/internal/sync/web_view_profile_invalidation_provider_factory.h" #import "ios/web_view/internal/sync/web_view_profile_invalidation_provider_factory.h"
...@@ -186,7 +185,6 @@ void WebViewBrowserState::RegisterPrefs( ...@@ -186,7 +185,6 @@ void WebViewBrowserState::RegisterPrefs(
WebViewAccountPasswordStoreFactory::GetInstance(); WebViewAccountPasswordStoreFactory::GetInstance();
WebViewPasswordStoreFactory::GetInstance(); WebViewPasswordStoreFactory::GetInstance();
WebViewSigninClientFactory::GetInstance(); WebViewSigninClientFactory::GetInstance();
WebViewSigninErrorControllerFactory::GetInstance();
WebViewIdentityManagerFactory::GetInstance(); WebViewIdentityManagerFactory::GetInstance();
WebViewGCMProfileServiceFactory::GetInstance(); WebViewGCMProfileServiceFactory::GetInstance();
WebViewProfileInvalidationProviderFactory::GetInstance(); WebViewProfileInvalidationProviderFactory::GetInstance();
......
...@@ -17,6 +17,14 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -17,6 +17,14 @@ NS_ASSUME_NONNULL_BEGIN
// The error domain for sync errors. // The error domain for sync errors.
FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVSyncErrorDomain; FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVSyncErrorDomain;
// NSString description for the type of error.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorUserInfoKey const CWVSyncErrorDescriptionKey;
// NSString message describing the error in more detail.
FOUNDATION_EXPORT CWV_EXPORT NSErrorUserInfoKey const CWVSyncErrorMessageKey;
// NSValue wrapped BOOL indicating if the error is transient.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorUserInfoKey const CWVSyncErrorIsTransientKey;
// Possible error codes during syncing. // Possible error codes during syncing.
typedef NS_ENUM(NSInteger, CWVSyncError) { typedef NS_ENUM(NSInteger, CWVSyncError) {
......
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