Commit 4e795f2f authored by sdefresne's avatar sdefresne Committed by Commit bot

Upstream Chrome on iOS source code [8/11].

Upstream part of Chrome on iOS source code. Nothing is built yet,
just new files added. The files will be added to the build as part
of the last CL to avoid breaking downstream tree.

BUG=653086

Review-Url: https://codereview.chromium.org/2587023002
Cr-Commit-Position: refs/heads/master@{#439471}
parent 13443afc
// Copyright 2016 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_UI_SETTINGS_MATERIAL_CELL_CATALOG_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_MATERIAL_CELL_CATALOG_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
@interface MaterialCellCatalogViewController
: SettingsRootCollectionViewController
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_MATERIAL_CELL_CATALOG_VIEW_CONTROLLER_H_
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_NATIVE_APPS_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_NATIVE_APPS_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/storekit_launcher.h"
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
namespace net {
class URLRequestContextGetter;
} // namespace net
// Settings view controller that displays the list of native apps that Chrome
// can launch given a specific URL. From this view controller, native apps can
// be installed if they are not present on the device. If they are present, a
// switch lets the user opt-in to open automatically related links.
// Icons for apps are static resources on a server and are therefore retrieved
// and displayed asynchronously.
@interface NativeAppsCollectionViewController
: SettingsRootCollectionViewController<StoreKitLauncher>
// Designated initializer. |requestContextGetter| is kept as a weak reference,
// therefore must outlive the initialized instance.
- (id)initWithURLRequestContextGetter:
(net::URLRequestContextGetter*)requestContextGetter;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_NATIVE_APPS_COLLECTION_VIEW_CONTROLLER_H_
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_NATIVE_APPS_COLLECTION_VIEW_CONTROLLER_PRIVATE_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_NATIVE_APPS_COLLECTION_VIEW_CONTROLLER_PRIVATE_H_
#import "ios/chrome/browser/ui/settings/native_apps_collection_view_controller.h"
// This file is a private header for NativeAppsCollectionViewController.
// It exposes private details for testing purposes.
namespace settings {
// User actions.
typedef enum {
kNativeAppsActionDidNothing,
kNativeAppsActionClickedInstall,
kNativeAppsActionTurnedDefaultBrowserOn,
kNativeAppsActionTurnedDefaultBrowserOff,
kNativeAppsActionTurnedAutoOpenOn,
kNativeAppsActionTurnedAutoOpenOff,
kNativeAppsActionCount,
} NativeAppsAction;
} // namespace settings
// Arbitrary value to shift indices (from 0 to N), to a range that doesn't
// include 0, in order to not mistake the default value (0) with the first
// index. That way, an invalid (or default) tag value can be detected.
// That means that all app cells have a tag computed by adding their index and
// this shift.
extern const NSInteger kTagShift;
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_NATIVE_APPS_COLLECTION_VIEW_CONTROLLER_PRIVATE_H_
// 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.
#import "ios/chrome/browser/ui/settings/native_apps_collection_view_controller.h"
#import "ios/chrome/browser/ui/settings/native_apps_collection_view_controller_private.h"
#include <memory>
#include "base/compiler_specific.h"
#include "base/ios/block_types.h"
#include "base/test/histogram_tester.h"
#include "base/threading/thread_task_runner_handle.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#import "ios/chrome/browser/ui/settings/cells/native_app_item.h"
#import "ios/public/provider/chrome/browser/native_app_launcher/fake_native_app_metadata.h"
#import "ios/public/provider/chrome/browser/native_app_launcher/fake_native_app_whitelist_manager.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "ui/base/l10n/l10n_util.h"
@interface NativeAppsCollectionViewController (Testing)
@property(nonatomic, retain) NSArray* appsInSettings;
@property(nonatomic, assign) id<StoreKitLauncher> storeKitLauncher;
- (void)configureWithNativeAppWhiteListManager:
(id<NativeAppWhitelistManager>)nativeAppWhitelistManager;
- (void)autoOpenInAppChanged:(UISwitch*)switchControl;
- (void)installApp:(UIButton*)button;
- (void)recordUserAction:(settings::NativeAppsAction)action;
- (void)appDidInstall:(NSNotification*)note;
- (id<NativeAppMetadata>)nativeAppAtIndex:(NSUInteger)index;
@end
@interface MockNativeAppWhitelistManager : FakeNativeAppWhitelistManager
@end
@implementation MockNativeAppWhitelistManager
- (id)init {
self = [super init];
if (self) {
base::scoped_nsobject<FakeNativeAppMetadata> app1(
[[FakeNativeAppMetadata alloc] init]);
[app1 setAppName:@"App1"];
[app1 setAppId:@"1"];
[app1 setGoogleOwnedApp:YES];
base::scoped_nsobject<FakeNativeAppMetadata> app2(
[[FakeNativeAppMetadata alloc] init]);
[app2 setAppName:@"App2"];
[app2 setAppId:@"2"];
[app2 setGoogleOwnedApp:YES];
base::scoped_nsobject<FakeNativeAppMetadata> app3(
[[FakeNativeAppMetadata alloc] init]);
[app3 setAppName:@"App3"];
[app3 setAppId:@"3"];
[app3 setGoogleOwnedApp:YES];
base::scoped_nsobject<FakeNativeAppMetadata> notOwnedApp(
[[FakeNativeAppMetadata alloc] init]);
[notOwnedApp setAppName:@"NotOwnedApp"];
[notOwnedApp setAppId:@"999"];
[notOwnedApp setGoogleOwnedApp:NO];
[self setAppList:@[ app1, app2, notOwnedApp, app3 ]
tldList:nil
acceptStoreIDs:nil];
}
return self;
}
@end
@interface MockStoreKitLauncher : NSObject<StoreKitLauncher>
@end
@implementation MockStoreKitLauncher
- (void)openAppStore:(id<NativeAppMetadata>)metadata {
}
@end
namespace {
class NativeAppsCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
void SetUp() override {
CollectionViewControllerTest::SetUp();
request_context_getter_ = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get());
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
mock_whitelist_manager_.reset([[MockNativeAppWhitelistManager alloc] init]);
[native_apps_controller
configureWithNativeAppWhiteListManager:mock_whitelist_manager_];
}
CollectionViewController* NewController() override NS_RETURNS_RETAINED {
DCHECK(request_context_getter_.get());
return [[NativeAppsCollectionViewController alloc]
initWithURLRequestContextGetter:request_context_getter_.get()];
}
// Runs the block and checks that the |action| (and only the action) has been
// recorded.
void ExpectUserActionAfterBlock(settings::NativeAppsAction action,
ProceduralBlock block) {
std::unique_ptr<base::HistogramTester> histogram_tester(
new base::HistogramTester());
block();
histogram_tester->ExpectUniqueSample("NativeAppLauncher.Settings", action,
1);
}
// Adds a mocked app of class MockNativeAppMetadata at the end of the apps'
// list.
void AddMockedApp() {
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
// Add a mock app at the end of the app list.
NSMutableArray* apps =
[NSMutableArray arrayWithArray:[native_apps_controller appsInSettings]];
ASSERT_GT([apps count], 0U);
base::scoped_nsobject<FakeNativeAppMetadata> installed_app(
[[FakeNativeAppMetadata alloc] init]);
[installed_app setAppName:@"App4"];
[installed_app setAppId:@"4"];
[installed_app setGoogleOwnedApp:YES];
[installed_app resetInfobarHistory];
[installed_app unsetShouldAutoOpenLinks];
[installed_app setInstalled:YES];
[apps addObject:installed_app];
[native_apps_controller setAppsInSettings:apps];
}
// Checks that the object's state is consistent with the represented app
// metadata at |index| in |section|.
void CheckNativeAppItem(NSUInteger section, NSUInteger index) {
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
id<NativeAppMetadata> metadata =
[native_apps_controller nativeAppAtIndex:index];
NativeAppItem* item = GetCollectionViewItem(section, index);
EXPECT_TRUE(item);
EXPECT_NSEQ([metadata appName], item.name);
if ([metadata isInstalled]) {
if ([metadata shouldAutoOpenLinks])
EXPECT_EQ(NativeAppItemSwitchOn, item.state);
else
EXPECT_EQ(NativeAppItemSwitchOff, item.state);
} else {
EXPECT_EQ(NativeAppItemInstall, item.state);
}
}
web::TestWebThreadBundle thread_bundle_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
base::scoped_nsobject<MockNativeAppWhitelistManager> mock_whitelist_manager_;
};
// Tests the integrity of the loaded model: section titles, sections and rows,
// along with checking that objects are correctly set up and appear in the
// correct order.
TEST_F(NativeAppsCollectionViewControllerTest, TestModel) {
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
CheckController();
EXPECT_EQ(2, NumberOfSections());
// As many rows as there are apps.
NSInteger apps_count = [[native_apps_controller appsInSettings] count];
EXPECT_EQ(apps_count, NumberOfItemsInSection(1));
for (NSInteger i = 0; i < apps_count; i++) {
CheckNativeAppItem(1, i);
}
}
// Tests that native app metadata are correctly updated when the user toggles
// switches. It checks that the appropriate UMA is sent for this user action.
TEST_F(NativeAppsCollectionViewControllerTest, AutoOpenInAppChanged) {
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
AddMockedApp();
// Make sure the last app is installed.
NSInteger last_index = [[native_apps_controller appsInSettings] count] - 1;
id<NativeAppMetadata> last_app =
[native_apps_controller nativeAppAtIndex:last_index];
ASSERT_TRUE([last_app isInstalled]);
EXPECT_FALSE([last_app shouldAutoOpenLinks]);
UISwitch* switch_from_cell = [[[UISwitch alloc] init] autorelease];
switch_from_cell.on = YES;
switch_from_cell.tag = kTagShift + last_index;
ExpectUserActionAfterBlock(settings::kNativeAppsActionTurnedAutoOpenOn, ^{
[native_apps_controller autoOpenInAppChanged:switch_from_cell];
EXPECT_TRUE([last_app shouldAutoOpenLinks]);
});
switch_from_cell.on = NO;
ExpectUserActionAfterBlock(settings::kNativeAppsActionTurnedAutoOpenOff, ^{
[native_apps_controller autoOpenInAppChanged:switch_from_cell];
EXPECT_FALSE([last_app shouldAutoOpenLinks]);
});
}
// Tests that the App Store is launched when the user clicks on an Install
// button. It checks that the appropriate UMA is sent for this user action.
TEST_F(NativeAppsCollectionViewControllerTest, InstallApp) {
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
id<StoreKitLauncher> real_opener = [native_apps_controller storeKitLauncher];
[native_apps_controller
setStoreKitLauncher:[[[MockStoreKitLauncher alloc] init] autorelease]];
UIButton* button_from_cell = [UIButton buttonWithType:UIButtonTypeCustom];
button_from_cell.tag = kTagShift;
id mock_button = [OCMockObject partialMockForObject:button_from_cell];
ExpectUserActionAfterBlock(settings::kNativeAppsActionClickedInstall, ^{
[native_apps_controller installApp:mock_button];
});
[mock_button verify];
[native_apps_controller setStoreKitLauncher:real_opener];
}
// Tests that native app metadata are correctly updated when the related app has
// been installed.
TEST_F(NativeAppsCollectionViewControllerTest, AppDidInstall) {
NativeAppsCollectionViewController* native_apps_controller =
static_cast<NativeAppsCollectionViewController*>(controller());
AddMockedApp();
// Make sure the last app can open any URL.
id<NativeAppMetadata> last_app =
[[native_apps_controller appsInSettings] lastObject];
ASSERT_TRUE([last_app canOpenURL:GURL()]);
EXPECT_FALSE([last_app shouldAutoOpenLinks]);
[native_apps_controller
appDidInstall:[NSNotification notificationWithName:@"App4" object:nil]];
EXPECT_TRUE([last_app shouldAutoOpenLinks]);
[last_app unsetShouldAutoOpenLinks];
}
} // namespace
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSPHRASE_COLLECTION_VIEW_CONTROLLER_TEST_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSPHRASE_COLLECTION_VIEW_CONTROLLER_TEST_H_
#include <memory>
#include "base/compiler_specific.h"
#include "base/mac/scoped_nsobject.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
#include "google_apis/gaia/google_service_auth_error.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/platform_test.h"
namespace browser_sync {
class ProfileSyncServiceMock;
} // namespace browser_sync
namespace web {
class BrowserState;
} // namespace web
class TestChromeBrowserState;
@class UINavigationController;
@class UIViewController;
// Base class for PassphraseCollectionViewController tests.
// Sets up a testing profile and a mock profile sync service, along with the
// supporting structure they require.
class PassphraseCollectionViewControllerTest
: public CollectionViewControllerTest {
public:
static std::unique_ptr<KeyedService> CreateNiceProfileSyncServiceMock(
web::BrowserState* context);
PassphraseCollectionViewControllerTest();
~PassphraseCollectionViewControllerTest() override;
protected:
void SetUp() override;
void SetUpNavigationController(UIViewController* test_controller);
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
// Weak, owned by chrome_browser_state_.
browser_sync::ProfileSyncServiceMock* fake_sync_service_;
// Default return values for NiceMock<browser_sync::ProfileSyncServiceMock>.
GoogleServiceAuthError default_auth_error_;
syncer::SyncCycleSnapshot default_sync_cycle_snapshot_;
// Dummy navigation stack for testing self-removal.
// Only valid when SetUpNavigationController has been called.
base::scoped_nsobject<UIViewController> dummy_controller_;
base::scoped_nsobject<UINavigationController> nav_controller_;
};
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_PASSPHRASE_COLLECTION_VIEW_CONTROLLER_TEST_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/ui/settings/passphrase_collection_view_controller_test.h"
#import <UIKit/UIKit.h>
#include <memory>
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "components/browser_sync/profile_sync_service_mock.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/prefs/browser_prefs.h"
#include "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/signin/authentication_service_fake.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.h"
#include "ios/chrome/browser/sync/sync_setup_service.h"
#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#include "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
using testing::DefaultValue;
using testing::NiceMock;
using testing::Return;
std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
sync_preferences::PrefServiceMockFactory factory;
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
new user_prefs::PrefRegistrySyncable);
std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs =
factory.CreateSyncable(registry.get());
RegisterBrowserStatePrefs(registry.get());
return prefs;
}
std::unique_ptr<KeyedService>
PassphraseCollectionViewControllerTest::CreateNiceProfileSyncServiceMock(
web::BrowserState* context) {
browser_sync::ProfileSyncService::InitParams init_params =
CreateProfileSyncServiceParamsForTest(
nullptr, ios::ChromeBrowserState::FromBrowserState(context));
return base::MakeUnique<NiceMock<browser_sync::ProfileSyncServiceMock>>(
&init_params);
}
PassphraseCollectionViewControllerTest::PassphraseCollectionViewControllerTest()
: CollectionViewControllerTest(),
fake_sync_service_(NULL),
default_auth_error_(GoogleServiceAuthError::NONE) {}
PassphraseCollectionViewControllerTest::
~PassphraseCollectionViewControllerTest() {}
void PassphraseCollectionViewControllerTest::SetUp() {
CollectionViewControllerTest::SetUp();
// Set up the default return values for non-trivial return types.
DefaultValue<const GoogleServiceAuthError&>::Set(default_auth_error_);
DefaultValue<syncer::SyncCycleSnapshot>::Set(default_sync_cycle_snapshot_);
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.AddTestingFactory(
AuthenticationServiceFactory::GetInstance(),
AuthenticationServiceFake::CreateAuthenticationService);
test_cbs_builder.SetPrefService(CreatePrefService());
chrome_browser_state_ = test_cbs_builder.Build();
fake_sync_service_ = static_cast<browser_sync::ProfileSyncServiceMock*>(
IOSChromeProfileSyncServiceFactory::GetInstance()
->SetTestingFactoryAndUse(chrome_browser_state_.get(),
CreateNiceProfileSyncServiceMock));
ON_CALL(*fake_sync_service_, GetRegisteredDataTypes())
.WillByDefault(Return(syncer::ModelTypeSet()));
fake_sync_service_->Initialize();
// Set up non-default return values for our sync service mock.
ON_CALL(*fake_sync_service_, IsPassphraseRequired())
.WillByDefault(Return(true));
ON_CALL(*fake_sync_service_, IsEngineInitialized())
.WillByDefault(Return(true));
ios::FakeChromeIdentityService* identityService =
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
identityService->AddIdentities(@[ @"identity1" ]);
ChromeIdentity* identity =
[identityService->GetAllIdentitiesSortedForDisplay() objectAtIndex:0];
AuthenticationServiceFactory::GetForBrowserState(chrome_browser_state_.get())
->SignIn(identity, "");
}
void PassphraseCollectionViewControllerTest::SetUpNavigationController(
UIViewController* test_controller) {
dummy_controller_.reset([[UIViewController alloc] init]);
nav_controller_.reset([[SettingsNavigationController alloc]
initWithRootViewController:dummy_controller_
browserState:chrome_browser_state_.get()
delegate:nil]);
[nav_controller_ pushViewController:test_controller animated:NO];
}
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
namespace autofill {
struct PasswordForm;
} // namespace autofill
@protocol ReauthenticationProtocol;
@protocol PasswordDetailsCollectionViewControllerDelegate<NSObject>
- (void)deletePassword:(const autofill::PasswordForm&)passwordForm;
@end
@interface PasswordDetailsCollectionViewController
: SettingsRootCollectionViewController
// The designated initializer. |delegate| and |reauthenticaionModule| must not
// be nil.
- (instancetype)
initWithPasswordForm:(autofill::PasswordForm)passwordForm
delegate:
(id<PasswordDetailsCollectionViewControllerDelegate>)delegate
reauthenticationModule:(id<ReauthenticationProtocol>)reauthenticationModule
username:(NSString*)username
password:(NSString*)password
origin:(NSString*)origin NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_DETAILS_COLLECTION_VIEW_CONTROLLER_H_
// 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.
#import "ios/chrome/browser/ui/settings/password_details_collection_view_controller.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#import "ios/chrome/browser/ui/settings/cells/password_details_item.h"
#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
#import "ios/chrome/browser/web/chrome_web_test.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "ui/base/l10n/l10n_util.h"
@interface MockReauthenticationModule : NSObject<ReauthenticationProtocol>
@property(nonatomic, copy) NSString* localizedReasonForAuthentication;
@end
@implementation MockReauthenticationModule
@synthesize localizedReasonForAuthentication =
_localizedReasonForAuthentication;
- (BOOL)canAttemptReauth {
return YES;
}
- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
handler:(void (^)(BOOL success))
showCopyPasswordsHandler {
self.localizedReasonForAuthentication = localizedReason;
showCopyPasswordsHandler(YES);
}
@end
@interface MockSavePasswordsCollectionViewController
: NSObject<PasswordDetailsCollectionViewControllerDelegate>
- (void)deletePassword:(const autofill::PasswordForm&)passwordForm;
@property(nonatomic) NSInteger numberOfCallsToDeletePassword;
@end
@implementation MockSavePasswordsCollectionViewController
@synthesize numberOfCallsToDeletePassword = _numberOfCallsToDeletePassword;
- (void)deletePassword:(const autofill::PasswordForm&)passwordForm {
++_numberOfCallsToDeletePassword;
}
@end
namespace {
NSString* kUsername = @"testusername";
NSString* kPassword = @"testpassword";
int kShowButtonItem = 1;
int kHideButtonItem = 2;
int kCopyButtonItem = 3;
int kDeleteButtonItem = 4;
int kUsernameSection = 0;
int kUsernameItem = 0;
int kPasswordSection = 1;
int kPasswordItem = 0;
class PasswordDetailsCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
PasswordDetailsCollectionViewControllerTest()
: thread_bundle_(web::TestWebThreadBundle::REAL_DB_THREAD) {}
void SetUp() override {
CollectionViewControllerTest::SetUp();
origin_ = @"testorigin.com";
delegate_.reset([[MockSavePasswordsCollectionViewController alloc] init]);
reauthenticationModule_.reset([[MockReauthenticationModule alloc] init]);
}
CollectionViewController* NewController() override NS_RETURNS_RETAINED {
return [[PasswordDetailsCollectionViewController alloc]
initWithPasswordForm:*(new autofill::PasswordForm())
delegate:delegate_
reauthenticationModule:reauthenticationModule_
username:kUsername
password:kPassword
origin:origin_];
}
void CreateControllerWithOrigin(NSString* test_origin) {
origin_ = test_origin;
CreateController();
}
web::TestWebThreadBundle thread_bundle_;
base::scoped_nsobject<MockSavePasswordsCollectionViewController> delegate_;
base::scoped_nsobject<MockReauthenticationModule> reauthenticationModule_;
NSString* origin_;
};
TEST_F(PasswordDetailsCollectionViewControllerTest, TestInitialization) {
CreateController();
CheckController();
EXPECT_EQ(2, NumberOfSections());
// Username section
EXPECT_EQ(1, NumberOfItemsInSection(kUsernameSection));
CheckSectionHeaderWithId(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME,
kUsernameSection);
PasswordDetailsItem* usernameItem =
GetCollectionViewItem(kUsernameSection, kUsernameItem);
EXPECT_NSEQ(kUsername, usernameItem.text);
EXPECT_TRUE(usernameItem.showingText);
// Password section
EXPECT_EQ(5, NumberOfItemsInSection(kPasswordSection));
CheckSectionHeaderWithId(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD,
kPasswordSection);
PasswordDetailsItem* passwordItem =
GetCollectionViewItem(kPasswordSection, kPasswordItem);
EXPECT_NSEQ(kPassword, passwordItem.text);
EXPECT_FALSE(passwordItem.showingText);
CheckTextCellTitleWithId(IDS_IOS_SETTINGS_PASSWORD_SHOW_BUTTON,
kPasswordSection, kShowButtonItem);
CheckTextCellTitleWithId(IDS_IOS_SETTINGS_PASSWORD_HIDE_BUTTON,
kPasswordSection, kHideButtonItem);
CheckTextCellTitleWithId(IDS_IOS_SETTINGS_PASSWORD_COPY_BUTTON,
kPasswordSection, kCopyButtonItem);
CheckTextCellTitleWithId(IDS_IOS_SETTINGS_PASSWORD_DELETE_BUTTON,
kPasswordSection, kDeleteButtonItem);
}
struct SimplifyOriginTestData {
NSString* origin;
NSString* expectedSimplifiedOrigin;
};
TEST_F(PasswordDetailsCollectionViewControllerTest, SimplifyOrigin) {
SimplifyOriginTestData test_data[] = {
{@"http://test.com/index.php", @"test.com"},
{@"test.com/index.php", @"test.com"},
{@"test.com", @"test.com"}};
for (size_t i = 0; i < arraysize(test_data); i++) {
SimplifyOriginTestData& data = test_data[i];
CreateControllerWithOrigin(data.origin);
EXPECT_NSEQ(data.expectedSimplifiedOrigin, controller().title)
<< " for origin " << base::SysNSStringToUTF8(test_data[i].origin);
ResetController();
}
}
TEST_F(PasswordDetailsCollectionViewControllerTest, ShowPassword) {
CreateController();
[controller() collectionView:[controller() collectionView]
didSelectItemAtIndexPath:[NSIndexPath indexPathForRow:kShowButtonItem
inSection:kPasswordSection]];
PasswordDetailsItem* passwordItem =
GetCollectionViewItem(kPasswordSection, kPasswordItem);
EXPECT_NSEQ(kPassword, passwordItem.text);
EXPECT_TRUE(passwordItem.showingText);
EXPECT_NSEQ(
l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_REAUTH_REASON_SHOW),
reauthenticationModule_.get().localizedReasonForAuthentication);
}
TEST_F(PasswordDetailsCollectionViewControllerTest, HidePassword) {
CreateController();
[controller() collectionView:[controller() collectionView]
didSelectItemAtIndexPath:[NSIndexPath indexPathForRow:kHideButtonItem
inSection:kPasswordSection]];
PasswordDetailsItem* passwordItem =
GetCollectionViewItem(kPasswordSection, kPasswordItem);
EXPECT_NSEQ(kPassword, passwordItem.text);
EXPECT_FALSE(passwordItem.showingText);
}
TEST_F(PasswordDetailsCollectionViewControllerTest, CopyPassword) {
CreateController();
[controller() collectionView:[controller() collectionView]
didSelectItemAtIndexPath:[NSIndexPath indexPathForRow:kCopyButtonItem
inSection:kPasswordSection]];
UIPasteboard* generalPasteboard = [UIPasteboard generalPasteboard];
EXPECT_NSEQ(kPassword, generalPasteboard.string);
EXPECT_NSEQ(
l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_REAUTH_REASON_COPY),
reauthenticationModule_.get().localizedReasonForAuthentication);
}
TEST_F(PasswordDetailsCollectionViewControllerTest, DeletePassword) {
CreateController();
[controller() collectionView:[controller() collectionView]
didSelectItemAtIndexPath:[NSIndexPath indexPathForRow:kDeleteButtonItem
inSection:kPasswordSection]];
EXPECT_EQ(1, delegate_.get().numberOfCallsToDeletePassword);
}
} // namespace
// Copyright 2016 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_UI_SETTINGS_PHYSICAL_WEB_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_PHYSICAL_WEB_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
#include "components/prefs/pref_service.h"
// This View Controller is responsible for managing the settings related to
// the Physical Web.
@interface PhysicalWebCollectionViewController
: SettingsRootCollectionViewController
// The designated initializer.
- (instancetype)initWithPrefs:(PrefService*)prefs NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
NS_UNAVAILABLE;
// Given the current Physical Web preference state, determine whether the
// preference should be rendered as enabled.
+ (BOOL)shouldEnableForPreferenceState:(int)preferenceState;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_PHYSICAL_WEB_COLLECTION_VIEW_CONTROLLER_H_
// 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.
#import "ios/chrome/browser/ui/settings/physical_web_collection_view_controller.h"
#import <CoreLocation/CoreLocation.h>
#include "base/ios/weak_nsobject.h"
#import "base/mac/foundation_util.h"
#import "base/mac/scoped_nsobject.h"
#include "base/metrics/user_metrics.h"
#include "components/google/core/browser/google_util.h"
#include "components/physical_web/data_source/physical_web_data_source.h"
#include "components/prefs/pref_member.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/physical_web/physical_web_constants.h"
#include "ios/chrome/browser/pref_names.h"
#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
#import "ios/chrome/browser/ui/settings/settings_utils.h"
#include "ios/chrome/common/string_util.h"
#include "ios/chrome/grit/ios_chromium_strings.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
namespace {
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierPhysicalWeb = kSectionIdentifierEnumZero,
SectionIdentifierLearnMore,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypePhysicalWebSwitch = kItemTypeEnumZero,
ItemTypeLearnMore,
};
} // namespace
@interface PhysicalWebCollectionViewController () {
// Pref for the Physical Web enabled state.
IntegerPrefMember _physicalWebEnabled;
}
// Called when the preference switch is toggled on or off.
- (void)physicalWebSwitched:(UISwitch*)switchView;
// Updates the Physical Web preference.
- (void)updatePhysicalWebEnabled:(BOOL)enabled;
@end
@implementation PhysicalWebCollectionViewController
#pragma mark - Initialization
- (instancetype)initWithPrefs:(PrefService*)prefs {
self = [super initWithStyle:CollectionViewControllerStyleAppBar];
if (self) {
self.title = l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_PHYSICAL_WEB);
_physicalWebEnabled.Init(prefs::kIosPhysicalWebEnabled, prefs);
[self loadModel];
}
return self;
}
- (instancetype)init {
NOTREACHED();
return nil;
}
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style {
NOTREACHED();
return nil;
}
#pragma mark - Preference switch
+ (BOOL)shouldEnableForPreferenceState:(int)preferenceState {
// In the default (onboarding) state, render the preference as On if the
// location app permission is granted.
if (preferenceState == physical_web::kPhysicalWebOnboarding) {
CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
return (authStatus == kCLAuthorizationStatusAuthorizedWhenInUse ||
authStatus == kCLAuthorizationStatusAuthorizedAlways);
}
return (preferenceState == physical_web::kPhysicalWebOn);
}
- (void)physicalWebSwitched:(UISwitch*)switchView {
[self updatePhysicalWebEnabled:switchView.isOn];
}
- (void)updatePhysicalWebEnabled:(BOOL)enabled {
_physicalWebEnabled.SetValue(enabled ? physical_web::kPhysicalWebOn
: physical_web::kPhysicalWebOff);
physical_web::PhysicalWebDataSource* dataSource =
GetApplicationContext()->GetPhysicalWebDataSource();
if (enabled) {
base::RecordAction(
base::UserMetricsAction("PhysicalWeb.Prefs.FeatureEnabled"));
if (dataSource) {
dataSource->StartDiscovery(true);
}
} else {
base::RecordAction(
base::UserMetricsAction("PhysicalWeb.Prefs.FeatureDisabled"));
if (dataSource) {
dataSource->StopDiscovery();
}
}
}
#pragma mark - UICollectionViewDataSource
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
cellForItemAtIndexPath:(NSIndexPath*)indexPath {
UICollectionViewCell* cell =
[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
switch (type) {
case ItemTypePhysicalWebSwitch: {
CollectionViewSwitchCell* switchCell =
base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell);
[switchCell.switchView addTarget:self
action:@selector(physicalWebSwitched:)
forControlEvents:UIControlEventValueChanged];
break;
}
}
return cell;
}
#pragma mark - UICollectionViewDelegate
- (BOOL)collectionView:(UICollectionView*)collectionView
shouldHighlightItemAtIndexPath:(NSIndexPath*)indexPath {
NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
switch (type) {
case ItemTypePhysicalWebSwitch:
case ItemTypeLearnMore:
return NO;
default:
return [super collectionView:collectionView
shouldHighlightItemAtIndexPath:indexPath];
}
}
#pragma mark - SettingsRootCollectionViewController
- (void)loadModel {
[super loadModel];
CollectionViewModel* model = self.collectionViewModel;
[model addSectionWithIdentifier:SectionIdentifierPhysicalWeb];
NSString* switchLabelText =
l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_PHYSICAL_WEB);
CollectionViewSwitchItem* switchItem = [[[CollectionViewSwitchItem alloc]
initWithType:ItemTypePhysicalWebSwitch] autorelease];
switchItem.text = switchLabelText;
switchItem.on = [PhysicalWebCollectionViewController
shouldEnableForPreferenceState:_physicalWebEnabled.GetValue()];
[model addItem:switchItem
toSectionWithIdentifier:SectionIdentifierPhysicalWeb];
[model addSectionWithIdentifier:SectionIdentifierLearnMore];
NSString* learnMoreText =
l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_PHYSICAL_WEB_DETAILS);
CollectionViewFooterItem* learnMore = [[[CollectionViewFooterItem alloc]
initWithType:ItemTypeLearnMore] autorelease];
learnMore.text = learnMoreText;
learnMore.linkURL = GURL(kPhysicalWebLearnMoreURL);
learnMore.linkDelegate = self;
learnMore.accessibilityTraits = UIAccessibilityTraitButton;
[model addItem:learnMore toSectionWithIdentifier:SectionIdentifierLearnMore];
}
#pragma mark - MDCCollectionViewStylingDelegate
- (MDCCollectionViewCellStyle)collectionView:(UICollectionView*)collectionView
cellStyleForSection:(NSInteger)section {
NSInteger sectionIdentifier =
[self.collectionViewModel sectionIdentifierForSection:section];
switch (sectionIdentifier) {
case SectionIdentifierLearnMore:
// Display the Learn More footer in the default style with no "card" UI
// and no section padding.
return MDCCollectionViewCellStyleDefault;
default:
return self.styler.cellStyle;
}
}
- (CGFloat)collectionView:(UICollectionView*)collectionView
cellHeightAtIndexPath:(NSIndexPath*)indexPath {
CollectionViewItem* item =
[self.collectionViewModel itemAtIndexPath:indexPath];
switch (item.type) {
case ItemTypeLearnMore:
return [MDCCollectionViewCell
cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
forItem:item];
default:
return MDCCellDefaultOneLineHeight;
}
}
- (BOOL)collectionView:(UICollectionView*)collectionView
shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath {
NSInteger sectionIdentifier =
[self.collectionViewModel sectionIdentifierForSection:indexPath.section];
switch (sectionIdentifier) {
case SectionIdentifierLearnMore:
// Display the Learn More footer without any background image or
// shadowing.
return YES;
default:
return NO;
}
}
- (BOOL)collectionView:(UICollectionView*)collectionView
hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
switch (type) {
case ItemTypePhysicalWebSwitch:
return YES;
default:
return NO;
}
}
@end
// Copyright 2016 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.
#import "ios/chrome/browser/ui/settings/physical_web_collection_view_controller.h"
#include <memory>
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "ios/chrome/browser/physical_web/physical_web_constants.h"
#include "ios/chrome/browser/pref_names.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#include "ios/chrome/grit/ios_chromium_strings.h"
#include "ios/chrome/grit/ios_strings.h"
@interface PhysicalWebCollectionViewController (ExposedForTesting)
- (void)updatePhysicalWebEnabled:(BOOL)enabled;
@end
namespace {
class PhysicalWebCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
void SetUp() override {
CollectionViewControllerTest::SetUp();
pref_service_ = CreateLocalState();
CreateController();
}
CollectionViewController* NewController() override {
physicalWebController_.reset([[PhysicalWebCollectionViewController alloc]
initWithPrefs:pref_service_.get()]);
return [physicalWebController_ retain];
}
std::unique_ptr<PrefService> CreateLocalState() {
scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple());
registry->RegisterIntegerPref(prefs::kIosPhysicalWebEnabled,
physical_web::kPhysicalWebOnboarding);
sync_preferences::PrefServiceMockFactory factory;
base::FilePath path("PhysicalWebCollectionViewControllerTest.pref");
factory.SetUserPrefsFile(path, message_loop_.task_runner().get());
return factory.Create(registry.get());
}
base::MessageLoopForUI message_loop_;
std::unique_ptr<PrefService> pref_service_;
base::scoped_nsobject<PhysicalWebCollectionViewController>
physicalWebController_;
};
// Tests PhysicalWebCollectionViewController is set up with all appropriate
// items and sections.
TEST_F(PhysicalWebCollectionViewControllerTest, TestModel) {
CheckController();
EXPECT_EQ(2, NumberOfSections());
// First section should have no section header and one row (switch item).
EXPECT_EQ(1, NumberOfItemsInSection(0));
CheckSwitchCellStateAndTitleWithId(NO, IDS_IOS_OPTIONS_ENABLE_PHYSICAL_WEB, 0,
0);
// Section section should have no section header and one row (footer item).
EXPECT_EQ(1, NumberOfItemsInSection(1));
CheckSectionFooterWithId(IDS_IOS_OPTIONS_ENABLE_PHYSICAL_WEB_DETAILS, 1);
}
TEST_F(PhysicalWebCollectionViewControllerTest, TestUpdateCheckedState) {
CheckController();
ASSERT_EQ(2, NumberOfSections());
ASSERT_EQ(1, NumberOfItemsInSection(0));
[physicalWebController_
updatePhysicalWebEnabled:physical_web::kPhysicalWebOn];
}
} // namespace
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
namespace ios {
class ChromeBrowserState;
} // namespace ios
// The accessibility identifier of the privacy settings collection view.
extern NSString* const kPrivacyCollectionViewId;
@interface PrivacyCollectionViewController
: SettingsRootCollectionViewController
// |browserState| cannot be nil
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle*)style
NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_COLLECTION_VIEW_CONTROLLER_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.
#import "ios/chrome/browser/ui/settings/privacy_collection_view_controller.h"
#include <memory>
#include "base/ios/ios_util.h"
#include "base/memory/ptr_util.h"
#include "base/strings/sys_string_conversions.h"
#include "components/handoff/pref_names_ios.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "ios/chrome/browser/application_context.h"
#import "ios/chrome/browser/autofill/autofill_controller.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/experimental_flags.h"
#include "ios/chrome/browser/pref_names.h"
#include "ios/chrome/browser/prefs/browser_prefs.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#import "ios/chrome/browser/ui/contextual_search/touch_to_search_permissions_mediator.h"
#import "ios/chrome/browser/ui/settings/physical_web_collection_view_controller.h"
#include "ios/chrome/grit/ios_chromium_strings.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "ios/web/public/web_capabilities.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
NSString* const kSpdyProxyEnabled = @"SpdyProxyEnabled";
class PrivacyCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
void SetUp() override {
CollectionViewControllerTest::SetUp();
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.SetPrefService(CreatePrefService());
chrome_browser_state_ = test_cbs_builder.Build();
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
initialValueForSpdyProxyEnabled_.reset(
[[defaults valueForKey:kSpdyProxyEnabled] copy]);
[defaults setValue:@"Disabled" forKey:kSpdyProxyEnabled];
CreateController();
}
void TearDown() override {
if (initialValueForSpdyProxyEnabled_) {
[[NSUserDefaults standardUserDefaults]
setObject:initialValueForSpdyProxyEnabled_.get()
forKey:kSpdyProxyEnabled];
} else {
[[NSUserDefaults standardUserDefaults]
removeObjectForKey:kSpdyProxyEnabled];
}
CollectionViewControllerTest::TearDown();
}
// Makes a PrefService to be used by the test.
std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
new user_prefs::PrefRegistrySyncable);
RegisterBrowserStatePrefs(registry.get());
sync_preferences::PrefServiceMockFactory factory;
return factory.CreateSyncable(registry.get());
}
CollectionViewController* NewController() override {
return [[PrivacyCollectionViewController alloc]
initWithBrowserState:chrome_browser_state_.get()];
}
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
base::scoped_nsobject<NSString> initialValueForSpdyProxyEnabled_;
};
// Tests PrivacyCollectionViewController is set up with all appropriate items
// and sections.
TEST_F(PrivacyCollectionViewControllerTest, TestModel) {
CheckController();
EXPECT_EQ(4, NumberOfSections());
int sectionIndex = 0;
EXPECT_EQ(1, NumberOfItemsInSection(sectionIndex));
CheckSectionHeaderWithId(IDS_IOS_OPTIONS_CONTINUITY_LABEL, sectionIndex);
NSString* handoffSubtitle = chrome_browser_state_->GetPrefs()->GetBoolean(
prefs::kIosHandoffToOtherDevices)
? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
CheckTextCellTitleAndSubtitle(
l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_HANDOFF_TO_OTHER_DEVICES),
handoffSubtitle, sectionIndex, 0);
++sectionIndex;
NSInteger expectedRows = 1;
#if defined(GOOGLE_CHROME_BUILD)
expectedRows++;
#endif
if ([TouchToSearchPermissionsMediator isTouchToSearchAvailableOnDevice])
expectedRows++;
if (web::IsDoNotTrackSupported())
expectedRows++;
if (experimental_flags::IsPhysicalWebEnabled())
expectedRows++;
EXPECT_EQ(expectedRows, NumberOfItemsInSection(sectionIndex));
CheckSectionHeaderWithId(IDS_IOS_OPTIONS_WEB_SERVICES_LABEL, sectionIndex);
base::scoped_nsobject<TouchToSearchPermissionsMediator>
touchToSearchPermissions([[TouchToSearchPermissionsMediator alloc]
initWithBrowserState:chrome_browser_state_.get()]);
NSString* contextualSearchSubtitle =
([touchToSearchPermissions preferenceState] == TouchToSearch::DISABLED)
? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
int row = 0;
CheckSwitchCellStateAndTitleWithId(
YES, IDS_IOS_OPTIONS_SEARCH_URL_SUGGESTIONS, sectionIndex, row++);
if ([TouchToSearchPermissionsMediator isTouchToSearchAvailableOnDevice]) {
CheckTextCellTitleAndSubtitle(
l10n_util::GetNSString(IDS_IOS_CONTEXTUAL_SEARCH_TITLE),
contextualSearchSubtitle, sectionIndex, row++);
}
#if defined(GOOGLE_CHROME_BUILD)
CheckTextCellTitleWithId(IDS_IOS_OPTIONS_SEND_USAGE_DATA, sectionIndex,
row++);
#endif
if (web::IsDoNotTrackSupported()) {
NSString* doNotTrackSubtitle =
chrome_browser_state_->GetPrefs()->GetBoolean(prefs::kEnableDoNotTrack)
? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
CheckTextCellTitleAndSubtitle(
l10n_util::GetNSString(IDS_IOS_OPTIONS_DO_NOT_TRACK_MOBILE),
doNotTrackSubtitle, sectionIndex, row++);
}
if (experimental_flags::IsPhysicalWebEnabled()) {
NSInteger physicalWebState =
GetApplicationContext()->GetLocalState()->GetInteger(
prefs::kIosPhysicalWebEnabled);
BOOL physicalWebEnabled = [PhysicalWebCollectionViewController
shouldEnableForPreferenceState:physicalWebState];
NSString* physicalWebSubtitle =
physicalWebEnabled ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
CheckTextCellTitleAndSubtitle(
l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_PHYSICAL_WEB),
physicalWebSubtitle, sectionIndex, row++);
}
sectionIndex++;
EXPECT_EQ(1, NumberOfItemsInSection(sectionIndex));
CheckSectionFooterWithId(IDS_IOS_OPTIONS_PRIVACY_FOOTER, sectionIndex);
sectionIndex++;
EXPECT_EQ(1, NumberOfItemsInSection(sectionIndex));
CheckTextCellTitle(l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_TITLE),
sectionIndex, 0);
}
} // namespace
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_H_
#import <Foundation/Foundation.h>
#import "ios/chrome/browser/ui/settings/reauthentication_protocol.h"
@protocol SuccessfulReauthTimeAccessor<NSObject>
// Method meant to be called by the |ReauthenticationModule| to update
// the time of the last successful re-authentication.
- (void)updateSuccessfulReauthTime;
// Returns the time of the last successful re-authentication.
- (NSDate*)lastSuccessfulReauthTime;
@end
/**
* This is used by |PasswordsDetailsCollectionViewController| to re-authenticate
* the user before displaying the password in plain text, or allowing it to be
* copied.
*/
@interface ReauthenticationModule : NSObject<ReauthenticationProtocol>
// The designated initializer. |successfulReauthTimeAccessor| must not be nil.
- (instancetype)initWithSuccessfulReauthTimeAccessor:
(id<SuccessfulReauthTimeAccessor>)successfulReauthTimeAccessor
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_MODULE_H_
// 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.
#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
#import <LocalAuthentication/LocalAuthentication.h>
#import "base/ios/weak_nsobject.h"
#include "base/mac/scoped_nsobject.h"
@implementation ReauthenticationModule {
// Authentication context on which the authentication policy is evaluated.
base::scoped_nsobject<LAContext> _context;
// Accessor allowing the module to request the update of the time when the
// successful re-authentication was performed and to get the time of the last
// successful re-authentication.
base::WeakNSProtocol<id<SuccessfulReauthTimeAccessor>>
_successfulReauthTimeAccessor;
}
- (instancetype)initWithSuccessfulReauthTimeAccessor:
(id<SuccessfulReauthTimeAccessor>)successfulReauthTimeAccessor {
DCHECK(successfulReauthTimeAccessor);
self = [super init];
if (self) {
_context.reset([[LAContext alloc] init]);
_successfulReauthTimeAccessor.reset(successfulReauthTimeAccessor);
}
return self;
}
- (BOOL)canAttemptReauth {
// The authentication method is Touch ID or passcode.
return
[_context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil];
}
- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
handler:(void (^)(BOOL success))handler {
if ([self isPreviousAuthValid]) {
handler(YES);
return;
}
_context.reset([[LAContext alloc] init]);
// No fallback option is provided.
_context.get().localizedFallbackTitle = @"";
base::WeakNSObject<ReauthenticationModule> weakSelf(self);
void (^replyBlock)(BOOL, NSError*) = ^(BOOL success, NSError* error) {
dispatch_async(dispatch_get_main_queue(), ^{
base::scoped_nsobject<ReauthenticationModule> strongSelf(
[weakSelf retain]);
if (!strongSelf)
return;
if (success) {
[strongSelf.get()
->_successfulReauthTimeAccessor updateSuccessfulReauthTime];
}
handler(success);
});
};
[_context evaluatePolicy:LAPolicyDeviceOwnerAuthentication
localizedReason:localizedReason
reply:replyBlock];
}
- (BOOL)isPreviousAuthValid {
BOOL previousAuthValid = NO;
const int kIntervalForValidAuthInSeconds = 60;
NSDate* lastSuccessfulReauthTime =
[_successfulReauthTimeAccessor lastSuccessfulReauthTime];
if (lastSuccessfulReauthTime) {
NSDate* currentTime = [NSDate date];
NSTimeInterval timeSincePreviousSuccessfulAuth =
[currentTime timeIntervalSinceDate:lastSuccessfulReauthTime];
if (timeSincePreviousSuccessfulAuth < kIntervalForValidAuthInSeconds) {
previousAuthValid = YES;
}
}
return previousAuthValid;
}
@end
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_PROTOCOL_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_PROTOCOL_H_
#import <Foundation/Foundation.h>
@protocol ReauthenticationProtocol<NSObject>
// Checks whether Touch ID and/or passcode is enabled for the device.
- (BOOL)canAttemptReauth;
// Attempts to reauthenticate the user with Touch ID or passcode if Touch ID is
// not available and the device is running iOS 9. |handler|
// will take action depending on the result of the reauth attempt.
- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
handler:(void (^)(BOOL success))handler;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_REAUTHENTICATION_PROTOCOL_H_
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_SAVE_PASSWORDS_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_SAVE_PASSWORDS_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
namespace ios {
class ChromeBrowserState;
} // namespace ios
@interface SavePasswordsCollectionViewController
: SettingsRootCollectionViewController
// The designated initializer. |browserState| must not be nil.
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SAVE_PASSWORDS_COLLECTION_VIEW_CONTROLLER_H_
// 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.
#import "ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.h"
#include <memory>
#include "base/compiler_specific.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
@interface SavePasswordsCollectionViewController (InternalMethods)
- (void)onGetPasswordStoreResults:
(const std::vector<autofill::PasswordForm*>&)result;
@end
namespace {
class SavePasswordsCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
SavePasswordsCollectionViewControllerTest()
: thread_bundle_(web::TestWebThreadBundle::REAL_DB_THREAD) {}
void SetUp() override {
TestChromeBrowserState::Builder test_cbs_builder;
chrome_browser_state_ = test_cbs_builder.Build();
CollectionViewControllerTest::SetUp();
IOSChromePasswordStoreFactory::GetInstance()->SetTestingFactory(
chrome_browser_state_.get(),
&password_manager::BuildPasswordStore<
web::BrowserState, password_manager::MockPasswordStore>);
CreateController();
}
CollectionViewController* NewController() override NS_RETURNS_RETAINED {
return [[SavePasswordsCollectionViewController alloc]
initWithBrowserState:chrome_browser_state_.get()];
}
// Adds a form to SavePasswordsTableViewController.
void AddPasswordForm(autofill::PasswordForm* form) {
SavePasswordsCollectionViewController* save_password_controller =
static_cast<SavePasswordsCollectionViewController*>(controller());
std::vector<autofill::PasswordForm*> passwords;
passwords.push_back(form);
[save_password_controller onGetPasswordStoreResults:passwords];
}
// Creates and adds a saved password form.
void AddSavedForm() {
autofill::PasswordForm* form = new autofill::PasswordForm();
form->origin = GURL("http://www.example.com/accounts/LoginAuth");
form->action = GURL("http://www.example.com/accounts/Login");
form->username_element = base::ASCIIToUTF16("Email");
form->username_value = base::ASCIIToUTF16("test@egmail.com");
form->password_element = base::ASCIIToUTF16("Passwd");
form->password_value = base::ASCIIToUTF16("test");
form->submit_element = base::ASCIIToUTF16("signIn");
form->signon_realm = "http://www.example.com/";
form->preferred = false;
form->scheme = autofill::PasswordForm::SCHEME_HTML;
form->blacklisted_by_user = false;
AddPasswordForm(form);
}
// Creates and adds a blacklisted site form to never offer to save
// user's password to those sites.
void AddBlacklistedForm1() {
autofill::PasswordForm* form = new autofill::PasswordForm();
form->origin = GURL("http://www.secret.com/login");
form->action = GURL("http://www.secret.com/action");
form->username_element = base::ASCIIToUTF16("email");
form->username_value = base::ASCIIToUTF16("test@secret.com");
form->password_element = base::ASCIIToUTF16("password");
form->password_value = base::ASCIIToUTF16("cantsay");
form->submit_element = base::ASCIIToUTF16("signIn");
form->signon_realm = "http://www.secret.com/";
form->preferred = false;
form->scheme = autofill::PasswordForm::SCHEME_HTML;
form->blacklisted_by_user = true;
AddPasswordForm(form);
}
// Creates and adds another blacklisted site form to never offer to save
// user's password to those sites.
void AddBlacklistedForm2() {
autofill::PasswordForm* form = new autofill::PasswordForm();
form->origin = GURL("http://www.secret2.com/login");
form->action = GURL("http://www.secret2.com/action");
form->username_element = base::ASCIIToUTF16("email");
form->username_value = base::ASCIIToUTF16("test@secret2.com");
form->password_element = base::ASCIIToUTF16("password");
form->password_value = base::ASCIIToUTF16("cantsay");
form->submit_element = base::ASCIIToUTF16("signIn");
form->signon_realm = "http://www.secret2.com/";
form->preferred = false;
form->scheme = autofill::PasswordForm::SCHEME_HTML;
form->blacklisted_by_user = true;
AddPasswordForm(form);
}
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
};
// Tests default case has no saved sites and no blacklisted sites.
TEST_F(SavePasswordsCollectionViewControllerTest, TestInitialization) {
CheckController();
EXPECT_EQ(2, NumberOfSections());
}
// Tests adding one item in saved password section.
TEST_F(SavePasswordsCollectionViewControllerTest, AddSavedPasswords) {
AddSavedForm();
EXPECT_EQ(3, NumberOfSections());
EXPECT_EQ(1, NumberOfItemsInSection(2));
}
// Tests adding one item in blacklisted password section.
TEST_F(SavePasswordsCollectionViewControllerTest, AddBlacklistedPasswords) {
AddBlacklistedForm1();
EXPECT_EQ(3, NumberOfSections());
EXPECT_EQ(1, NumberOfItemsInSection(2));
}
// Tests adding one item in saved password section, and two items in blacklisted
// password section.
TEST_F(SavePasswordsCollectionViewControllerTest, AddSavedAndBlacklisted) {
AddSavedForm();
AddBlacklistedForm1();
AddBlacklistedForm2();
// There should be two sections added.
EXPECT_EQ(4, NumberOfSections());
// There should be 1 row in saved password section.
EXPECT_EQ(1, NumberOfItemsInSection(2));
// There should be 2 rows in blacklisted password section.
EXPECT_EQ(2, NumberOfItemsInSection(3));
}
// Tests deleting items from saved passwords and blacklisted passwords sections.
TEST_F(SavePasswordsCollectionViewControllerTest, DeleteItems) {
AddSavedForm();
AddBlacklistedForm1();
AddBlacklistedForm2();
void (^deleteItemWithWait)(int, int) = ^(int i, int j) {
__block BOOL completionCalled = NO;
this->DeleteItem(i, j, ^{
completionCalled = YES;
});
base::test::ios::WaitUntilCondition(^bool() {
return completionCalled;
});
};
// Delete item in save passwords section.
deleteItemWithWait(2, 0);
EXPECT_EQ(3, NumberOfSections());
// Section 2 should now be the blacklisted passwords section, and should still
// have both its items.
EXPECT_EQ(2, NumberOfItemsInSection(2));
// Delete item in blacklisted passwords section.
deleteItemWithWait(2, 0);
EXPECT_EQ(1, NumberOfItemsInSection(2));
deleteItemWithWait(2, 0);
// There should be no password sections remaining.
EXPECT_EQ(2, NumberOfSections());
}
} // namespace
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_SEARCH_ENGINE_SETTINGS_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_SEARCH_ENGINE_SETTINGS_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
namespace ios {
class ChromeBrowserState;
} // namespace ios
// This class is the table view for the Search Engine settings.
@interface SearchEngineSettingsCollectionViewController
: SettingsRootCollectionViewController
// The designated initializer. |browserState| must not be nil.
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SEARCH_ENGINE_SETTINGS_COLLECTION_VIEW_CONTROLLER_H_
// 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.
#import "ios/chrome/browser/ui/settings/search_engine_settings_collection_view_controller.h"
#include <memory>
#include "base/ios/weak_nsobject.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/template_url_service_observer.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
#include "ui/base/l10n/l10n_util_mac.h"
@interface SearchEngineSettingsCollectionViewController ()
- (void)onChange;
@end
namespace {
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierSearchEngines = kSectionIdentifierEnumZero,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeSearchEnginesEngine = kItemTypeEnumZero,
};
// Observer used to reload the Search Engine collection view once the
// TemplateURLService changes, either on first load or due to a
// policy change.
class SearchEngineObserver : public TemplateURLServiceObserver {
public:
SearchEngineObserver(SearchEngineSettingsCollectionViewController* owner,
TemplateURLService* urlService);
~SearchEngineObserver() override;
void OnTemplateURLServiceChanged() override;
private:
base::WeakNSObject<SearchEngineSettingsCollectionViewController> owner_;
TemplateURLService* templateURLService_; // weak
};
SearchEngineObserver::SearchEngineObserver(
SearchEngineSettingsCollectionViewController* owner,
TemplateURLService* urlService)
: owner_(owner), templateURLService_(urlService) {
templateURLService_->AddObserver(this);
}
SearchEngineObserver::~SearchEngineObserver() {
templateURLService_->RemoveObserver(this);
}
void SearchEngineObserver::OnTemplateURLServiceChanged() {
base::scoped_nsobject<SearchEngineSettingsCollectionViewController>
strongOwner([owner_.get() retain]);
[strongOwner onChange];
}
} // namespace
@implementation SearchEngineSettingsCollectionViewController {
TemplateURLService* templateURLService_; // weak
std::unique_ptr<SearchEngineObserver> observer_;
// Prevent unnecessary notifications when we write to the setting.
BOOL updatingBackend_;
}
#pragma mark Initialization
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
DCHECK(browserState);
self = [super initWithStyle:CollectionViewControllerStyleAppBar];
if (self) {
templateURLService_ =
ios::TemplateURLServiceFactory::GetForBrowserState(browserState);
observer_.reset(new SearchEngineObserver(self, templateURLService_));
templateURLService_->Load();
[self setTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_ENGINE_SETTING_TITLE)];
[self setCollectionViewAccessibilityIdentifier:@"Search Engine"];
[self loadModel];
}
return self;
}
- (void)loadModel {
[super loadModel];
CollectionViewModel* model = self.collectionViewModel;
NSArray* values = [self allValues];
// Do not add any sections if there are no search engines.
if (![values count]) {
return;
}
[model addSectionWithIdentifier:SectionIdentifierSearchEngines];
for (NSUInteger i = 0; i < values.count; i++) {
NSString* value = values[i];
BOOL checked = [value isEqualToString:[self currentValue]];
base::scoped_nsobject<CollectionViewTextItem> engine(
[[CollectionViewTextItem alloc]
initWithType:ItemTypeSearchEnginesEngine]);
[engine setText:value];
if (checked) {
[engine setAccessoryType:MDCCollectionViewCellAccessoryCheckmark];
}
[model addItem:engine
toSectionWithIdentifier:SectionIdentifierSearchEngines];
}
}
#pragma mark UICollectionViewDelegate
- (void)collectionView:(UICollectionView*)collectionView
didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
[super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
CollectionViewModel* model = self.collectionViewModel;
// Only handle taps on search engine items.
CollectionViewItem* selectedItem = [model itemAtIndexPath:indexPath];
if (selectedItem.type != ItemTypeSearchEnginesEngine) {
return;
}
// Do nothing if the tapped engine was already the default.
CollectionViewTextItem* selectedTextItem =
base::mac::ObjCCastStrict<CollectionViewTextItem>(selectedItem);
if (selectedTextItem.accessoryType ==
MDCCollectionViewCellAccessoryCheckmark) {
return;
}
// Iterate through the engines and remove the checkmark from any that have it.
NSMutableArray* modifiedItems = [NSMutableArray array];
for (CollectionViewItem* item in
[model itemsInSectionWithIdentifier:SectionIdentifierSearchEngines]) {
if (item.type != ItemTypeSearchEnginesEngine) {
continue;
}
CollectionViewTextItem* textItem =
base::mac::ObjCCastStrict<CollectionViewTextItem>(item);
if (textItem.accessoryType == MDCCollectionViewCellAccessoryCheckmark) {
textItem.accessoryType = MDCCollectionViewCellAccessoryNone;
[modifiedItems addObject:textItem];
}
}
// Show the checkmark on the new default engine.
CollectionViewTextItem* newDefaultEngine =
base::mac::ObjCCastStrict<CollectionViewTextItem>(
[model itemAtIndexPath:indexPath]);
newDefaultEngine.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
[modifiedItems addObject:newDefaultEngine];
// Set the new engine as the default.
[self setValueFromIndex:[model indexInItemTypeForIndexPath:indexPath]];
[self reconfigureCellsForItems:modifiedItems
inSectionWithIdentifier:SectionIdentifierSearchEngines];
}
#pragma mark Internal methods
- (NSArray*)allValues {
std::vector<TemplateURL*> urls = templateURLService_->GetTemplateURLs();
NSMutableArray* items = [NSMutableArray arrayWithCapacity:urls.size()];
for (std::vector<TemplateURL*>::const_iterator iter = urls.begin();
iter != urls.end(); ++iter) {
[items addObject:base::SysUTF16ToNSString((*iter)->short_name())];
}
return items;
}
- (NSString*)currentValue {
return base::SysUTF16ToNSString(
templateURLService_->GetDefaultSearchProvider()->short_name());
}
- (void)setValueFromIndex:(NSUInteger)index {
std::vector<TemplateURL*> urls = templateURLService_->GetTemplateURLs();
DCHECK_GE(index, 0U);
DCHECK_LT(index, urls.size());
updatingBackend_ = YES;
templateURLService_->SetUserSelectedDefaultSearchProvider(urls[index]);
updatingBackend_ = NO;
}
- (void)onChange {
if (!updatingBackend_)
[self reloadData];
}
@end
// 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.
#import "ios/chrome/browser/ui/settings/search_engine_settings_collection_view_controller.h"
#include <memory>
#include "base/compiler_specific.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/search_engines/template_url_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_controller_test.h"
#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
namespace {
class SearchEngineSettingsCollectionViewControllerTest
: public CollectionViewControllerTest {
protected:
void SetUp() override {
CollectionViewControllerTest::SetUp();
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.AddTestingFactory(
ios::TemplateURLServiceFactory::GetInstance(),
ios::TemplateURLServiceFactory::GetDefaultFactory());
chrome_browser_state_ = test_cbs_builder.Build();
DefaultSearchManager::SetFallbackSearchEnginesDisabledForTesting(true);
template_url_service_ = ios::TemplateURLServiceFactory::GetForBrowserState(
chrome_browser_state_.get());
template_url_service_->Load();
}
CollectionViewController* NewController() override NS_RETURNS_RETAINED {
return [[SearchEngineSettingsCollectionViewController alloc]
initWithBrowserState:chrome_browser_state_.get()];
}
std::unique_ptr<TemplateURL> NewTemplateUrl(const std::string& shortName) {
TemplateURLData data;
data.SetShortName(base::ASCIIToUTF16(shortName));
return std::unique_ptr<TemplateURL>(new TemplateURL(data));
}
void FillTemplateUrlService() {
TemplateURL* defaultProvider =
template_url_service_->Add(NewTemplateUrl("first_url"));
template_url_service_->SetUserSelectedDefaultSearchProvider(
defaultProvider);
template_url_service_->Add(NewTemplateUrl("second_url"));
template_url_service_->Add(NewTemplateUrl("third_url"));
}
void CheckModelMatchesTemplateURLs() {
TemplateURLService::TemplateURLVector urls =
template_url_service_->GetTemplateURLs();
EXPECT_EQ(1, NumberOfSections());
ASSERT_EQ(urls.size(),
static_cast<unsigned int>(NumberOfItemsInSection(0)));
for (unsigned int i = 0; i < urls.size(); ++i) {
BOOL isDefault =
template_url_service_->GetDefaultSearchProvider() == urls[i];
CheckTextCellTitle(base::SysUTF16ToNSString(urls[i]->short_name()), 0, i);
CollectionViewTextItem* textItem =
base::mac::ObjCCastStrict<CollectionViewTextItem>(
GetCollectionViewItem(0, i));
EXPECT_EQ(isDefault ? MDCCollectionViewCellAccessoryCheckmark
: MDCCollectionViewCellAccessoryNone,
textItem.accessoryType);
}
}
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
TemplateURLService* template_url_service_; // weak
};
TEST_F(SearchEngineSettingsCollectionViewControllerTest, TestNoUrl) {
CreateController();
CheckController();
EXPECT_EQ(0, NumberOfSections());
}
TEST_F(SearchEngineSettingsCollectionViewControllerTest, TestWithUrlsLoaded) {
FillTemplateUrlService();
CreateController();
CheckModelMatchesTemplateURLs();
}
TEST_F(SearchEngineSettingsCollectionViewControllerTest, TestWithAddedUrl) {
FillTemplateUrlService();
CreateController();
TemplateURL* newUrl = template_url_service_->Add(NewTemplateUrl("new_url"));
CheckModelMatchesTemplateURLs();
template_url_service_->SetUserSelectedDefaultSearchProvider(newUrl);
CheckModelMatchesTemplateURLs();
DCHECK(newUrl != template_url_service_->GetTemplateURLs()[0]);
template_url_service_->SetUserSelectedDefaultSearchProvider(
template_url_service_->GetTemplateURLs()[0]);
template_url_service_->Remove(newUrl);
CheckModelMatchesTemplateURLs();
}
TEST_F(SearchEngineSettingsCollectionViewControllerTest, TestChangeProvider) {
FillTemplateUrlService();
CreateController();
[controller() collectionView:[controller() collectionView]
didSelectItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
EXPECT_EQ(template_url_service_->GetTemplateURLs()[0],
template_url_service_->GetDefaultSearchProvider());
CheckModelMatchesTemplateURLs();
[controller() collectionView:[controller() collectionView]
didSelectItemAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]];
TemplateURL* url = template_url_service_->GetTemplateURLs()[1];
EXPECT_EQ(url, template_url_service_->GetDefaultSearchProvider());
// Check that the selection was written back to the prefs.
const base::DictionaryValue* searchProviderDict =
chrome_browser_state_->GetTestingPrefService()->GetDictionary(
DefaultSearchManager::kDefaultSearchProviderDataPrefName);
EXPECT_TRUE(searchProviderDict);
base::string16 shortName;
EXPECT_TRUE(searchProviderDict->GetString(DefaultSearchManager::kShortName,
&shortName));
EXPECT_EQ(url->short_name(), shortName);
CheckModelMatchesTemplateURLs();
}
} // namespace
// 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 IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COLLECTION_VIEW_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COLLECTION_VIEW_CONTROLLER_H_
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
@class SigninInteractionController;
namespace ios {
class ChromeBrowserState;
} // namespace ios
// The accessibility identifier of the settings collection view.
extern NSString* const kSettingsCollectionViewId;
// The accessibility identifier of the sign in cell.
extern NSString* const kSettingsSignInCellId;
// The accessibility identifier of the account cell.
extern NSString* const kSettingsAccountCellId;
// The accessibility identifier of the Search Engine cell.
extern NSString* const kSettingsSearchEngineCellId;
// The accessibility identifier of the Voice Search cell.
extern NSString* const kSettingsVoiceSearchCellId;
// This class is the collection view for the application settings.
@interface SettingsCollectionViewController
: SettingsRootCollectionViewController<SettingsControllerProtocol>
// The controller used to signin on this screen. Returns nil unless a signin is
// currently occuring.
@property(nonatomic, readonly)
SigninInteractionController* signinInteractionController;
// Initializes a new SettingsCollectionViewController. |mainBrowserState|,
// |currentBrowserState| and |dataSource| must not be nil.
// If |currentBrowserState| is not off the record, then it must be equal to
// |mainBrowserState|.
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)mainBrowserState
currentBrowserState:
(ios::ChromeBrowserState*)currentBrowserState
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COLLECTION_VIEW_CONTROLLER_H_
// Copyright 2016 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.
// ====== New Architecture =====
// = This code is only used in the new iOS Chrome architecture. =
// ============================================================================
#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COORDINATOR_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/browser_coordinator.h"
// Action delegate protocol for coordinators to handle changes to the
// Settings UI.
@protocol SettingsActionDelegate<NSObject>
// Close the settings UI.
- (void)closeSettings;
@end
// A coordinator for the Settings UI, which is usually presented modally
// on top of whatever other UI is currently active.
@interface SettingsCoordinator : BrowserCoordinator
// Action delegate for this coordinator.
@property(nonatomic, weak) id<SettingsActionDelegate> actionDelegate;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_COORDINATOR_H_
// Copyright 2016 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.
// ====== New Architecture =====
// = This code is only used in the new iOS Chrome architecture. =
// ============================================================================
#import "ios/chrome/browser/ui/settings/settings_coordinator.h"
#import "ios/chrome/browser/browser_coordinator+internal.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface SettingsCoordinator ()<SettingsNavigationControllerDelegate>
@property(nonatomic, strong) SettingsNavigationController* viewController;
@end
@implementation SettingsCoordinator
@synthesize actionDelegate = _actionDelegate;
@synthesize viewController = _viewController;
#pragma mark - BrowserCoordinator
- (void)start {
self.viewController = [SettingsNavigationController
newSettingsMainControllerWithMainBrowserState:self.browserState
currentBrowserState:self.browserState
delegate:self];
[self.rootViewController presentViewController:self.viewController
animated:YES
completion:nil];
}
- (void)stop {
[self.viewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - SettingsNavigationControllerDelegate
- (void)closeSettingsAndOpenUrl:(OpenUrlCommand*)command {
// Placeholder implementation to conform to the delegate protocol;
// for now this just closes the settings without opening a URL.
[self closeSettings];
}
- (void)closeSettingsAndOpenNewIncognitoTab {
// Placeholder implementation to conform to the delegate protocol;
// for now this just closes the settings without opening a new tab.
[self closeSettings];
}
- (void)closeSettings {
[self.actionDelegate closeSettings];
}
@end
This diff is collapsed.
// 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_UI_SETTINGS_SETTINGS_NAVIGATION_CONTROLLER_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_NAVIGATION_CONTROLLER_H_
#import <UIKit/UIKit.h>
@class OpenUrlCommand;
@protocol ImportDataControllerDelegate;
@protocol UserFeedbackDataSource;
namespace ios {
class ChromeBrowserState;
} // namespace ios
@protocol SettingsControllerProtocol<NSObject>
@optional
// Notifies the controller that the settings screen is being dismissed.
- (void)settingsWillBeDismissed;
@end
@protocol SettingsNavigationControllerDelegate<NSObject>
// Handles a close settings and open URL command.
- (void)closeSettingsAndOpenUrl:(OpenUrlCommand*)command;
// Informs the delegate that the settings navigation controller should be
// closed and a new incognito window should be opened.
- (void)closeSettingsAndOpenNewIncognitoTab;
// Informs the delegate that the settings navigation controller should be
// closed.
- (void)closeSettings;
@end
// Controller to modify user settings.
@interface SettingsNavigationController : UINavigationController
// Whether sync changes should be committed when the settings are being
// dismissed. Defaults to YES.
@property(nonatomic, assign) BOOL shouldCommitSyncChangesOnDismissal;
// Creates a new SettingsCollectionViewController and the chrome around it.
// |browserState| is used to personalize some settings aspects and should not be
// nil. |delegate| may be nil.
// clang-format off
+ (SettingsNavigationController*)newSettingsMainControllerWithMainBrowserState:
(ios::ChromeBrowserState*)browserState
currentBrowserState:
(ios::ChromeBrowserState*)currentBrowserState
delegate:
(id<SettingsNavigationControllerDelegate>)delegate;
// clang-format on
// Creates a new AccountsCollectionViewController and the chrome around it.
// |browserState| is used to personalize some settings aspects and should not be
// nil. |delegate| may be nil.
+ (SettingsNavigationController*)
newAccountsController:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)delegate;
// Creates a new SignInSettingsCollectionViewController and the chrome around
// it. |browserState| is used to personalize some settings aspects and should
// not be nil. |delegate| may be nil.
+ (SettingsNavigationController*)
newSyncController:(ios::ChromeBrowserState*)browserState
allowSwitchSyncAccount:(BOOL)allowSwitchSyncAccount
delegate:(id<SettingsNavigationControllerDelegate>)delegate;
// Creates a new SyncEncryptionPassphraseCollectionViewController and the chrome
// around it. |browserState| is used to personalize some settings aspects and
// should not be nil. |delegate| may be nil.
+ (SettingsNavigationController*)
newSyncEncryptionPassphraseController:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)
delegate;
// Creates a new NativeAppsCollectionViewController and the chrome around it.
// |browserState| is used to personalize some settings aspects and should not be
// nil. |delegate| may be nil.
+ (SettingsNavigationController*)
newNativeAppsController:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)delegate;
// Creates a new ClearBrowsingDataCollectionViewController and the chrome around
// it.
// |browserState| is used to personalize some settings aspects and should not be
// nil. |delegate| may be nil.
+ (SettingsNavigationController*)
newClearBrowsingDataController:(ios::ChromeBrowserState*)browserState
delegate:
(id<SettingsNavigationControllerDelegate>)delegate;
+ (SettingsNavigationController*)
newContextualSearchController:(ios::ChromeBrowserState*)browserState
delegate:
(id<SettingsNavigationControllerDelegate>)delegate;
// Creates a new SavePasswordsCollectionViewController and the chrome around it.
// |browserState| is used to personalize some settings aspects and should not be
// nil. |delegate| may be nil.
+ (SettingsNavigationController*)
newSavePasswordsController:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)delegate;
// Creates and displays a new UserFeedbackViewController. |browserState| is used
// to personalize some settings aspects and should not be nil. |dataSource| is
// used to populate the UserFeedbackViewController. |delegate| may be nil.
+ (SettingsNavigationController*)
newUserFeedbackController:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)delegate
feedbackDataSource:(id<UserFeedbackDataSource>)dataSource;
// Creates and displays a new ImportDataCollectionViewController. |browserState|
// should not be nil.
+ (SettingsNavigationController*)
newImportDataController:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)delegate
importDataDelegate:(id<ImportDataControllerDelegate>)importDataDelegate
fromEmail:(NSString*)fromEmail
toEmail:(NSString*)toEmail
isSignedIn:(BOOL)isSignedIn;
// Returns a new Done button for a UINavigationItem which will call
// closeSettings when it is pressed. Should only be called by view controllers
// owned by SettingsNavigationController.
- (UIBarButtonItem*)doneButton;
// Returns the current main browser state.
- (ios::ChromeBrowserState*)mainBrowserState;
// Notifies this |SettingsNavigationController| that it will be dismissed such
// that it has a possibility to do necessary clean up.
- (void)settingsWillBeDismissed;
// Closes this |SettingsNavigationController| by asking its delegate.
- (void)closeSettings;
// Pops the top view controller if there exists more than one view controller in
// the navigation stack. Closes the settings if the top view controller is the
// only view controller in the navigation stack.
- (void)popViewControllerOrCloseSettingsAnimated:(BOOL)animated;
@end
@interface SettingsNavigationController (ExposedForTesting)
// Initializes the UINavigationController with |rootViewController|.
// User of this class should not call the normal |initWithRootViewController|.
- (instancetype)
initWithRootViewController:(UIViewController*)rootViewController
browserState:(ios::ChromeBrowserState*)browserState
delegate:(id<SettingsNavigationControllerDelegate>)delegate;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_NAVIGATION_CONTROLLER_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.
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
#import <Foundation/Foundation.h>
#include <memory>
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
#include "base/memory/ptr_util.h"
#include "components/search_engines/template_url_service.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#include "ios/chrome/browser/signin/authentication_service.h"
#include "ios/chrome/browser/signin/authentication_service_factory.h"
#include "ios/chrome/browser/signin/authentication_service_fake.h"
#include "ios/chrome/browser/sync/sync_setup_service.h"
#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
#include "ios/chrome/test/testing_application_context.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
namespace {
NSString* const kSpdyProxyEnabled = @"SpdyProxyEnabled";
using testing::ReturnRef;
class SettingsNavigationControllerTest : public PlatformTest {
protected:
SettingsNavigationControllerTest()
: browser_state_manager_(base::FilePath()) {}
void SetUp() override {
PlatformTest::SetUp();
TestingApplicationContext::GetGlobal()->SetChromeBrowserStateManager(
&browser_state_manager_);
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.AddTestingFactory(
AuthenticationServiceFactory::GetInstance(),
&AuthenticationServiceFake::CreateAuthenticationService);
test_cbs_builder.AddTestingFactory(
ios::TemplateURLServiceFactory::GetInstance(),
ios::TemplateURLServiceFactory::GetDefaultFactory());
chrome_browser_state_ = test_cbs_builder.Build();
mockDelegate_.reset([[OCMockObject
niceMockForProtocol:@protocol(SettingsNavigationControllerDelegate)]
retain]);
TemplateURLService* template_url_service =
ios::TemplateURLServiceFactory::GetForBrowserState(
chrome_browser_state_.get());
template_url_service->Load();
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
initialValueForSpdyProxyEnabled_.reset(
[[defaults stringForKey:kSpdyProxyEnabled] copy]);
[defaults setObject:@"Disabled" forKey:kSpdyProxyEnabled];
};
void TearDown() override {
if (initialValueForSpdyProxyEnabled_) {
[[NSUserDefaults standardUserDefaults]
setObject:initialValueForSpdyProxyEnabled_.get()
forKey:kSpdyProxyEnabled];
} else {
[[NSUserDefaults standardUserDefaults]
removeObjectForKey:kSpdyProxyEnabled];
}
TestingApplicationContext::GetGlobal()->SetChromeBrowserStateManager(
nullptr);
PlatformTest::TearDown();
}
web::TestWebThreadBundle threadBundle_;
TestChromeBrowserStateManager browser_state_manager_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
base::mac::ScopedNSAutoreleasePool pool;
base::scoped_nsprotocol<id> mockDelegate_;
base::scoped_nsobject<NSString> initialValueForSpdyProxyEnabled_;
};
// When navigation stack has more than one view controller,
// -popViewControllerAnimated: successfully removes the top view controller.
TEST_F(SettingsNavigationControllerTest, PopController) {
base::scoped_nsobject<SettingsNavigationController> settingsController(
[SettingsNavigationController
newSettingsMainControllerWithMainBrowserState:chrome_browser_state_
.get()
currentBrowserState:chrome_browser_state_
.get()
delegate:nil]);
base::scoped_nsobject<UIViewController> viewController(
[[UIViewController alloc] initWithNibName:nil bundle:nil]);
[settingsController pushViewController:viewController animated:NO];
EXPECT_EQ(2U, [[settingsController viewControllers] count]);
UIViewController* poppedViewController =
[settingsController popViewControllerAnimated:NO];
EXPECT_NSEQ(viewController, poppedViewController);
EXPECT_EQ(1U, [[settingsController viewControllers] count]);
}
// When the navigation stack has only one view controller,
// -popViewControllerAnimated: returns false.
TEST_F(SettingsNavigationControllerTest, DontPopRootController) {
base::scoped_nsobject<SettingsNavigationController> settingsController(
[SettingsNavigationController
newSettingsMainControllerWithMainBrowserState:chrome_browser_state_
.get()
currentBrowserState:chrome_browser_state_
.get()
delegate:nil]);
EXPECT_EQ(1U, [[settingsController viewControllers] count]);
EXPECT_FALSE([settingsController popViewControllerAnimated:NO]);
}
// When the settings navigation stack has more than one view controller, calling
// -popViewControllerOrCloseSettingsAnimated: pops the top view controller to
// reveal the view controller underneath.
TEST_F(SettingsNavigationControllerTest,
PopWhenNavigationStackSizeIsGreaterThanOne) {
base::scoped_nsobject<SettingsNavigationController> settingsController(
[SettingsNavigationController
newSettingsMainControllerWithMainBrowserState:chrome_browser_state_
.get()
currentBrowserState:chrome_browser_state_
.get()
delegate:mockDelegate_]);
base::scoped_nsobject<UIViewController> viewController(
[[UIViewController alloc] initWithNibName:nil bundle:nil]);
[settingsController pushViewController:viewController animated:NO];
EXPECT_EQ(2U, [[settingsController viewControllers] count]);
[[mockDelegate_ reject] closeSettings];
[settingsController popViewControllerOrCloseSettingsAnimated:NO];
EXPECT_EQ(1U, [[settingsController viewControllers] count]);
EXPECT_OCMOCK_VERIFY(mockDelegate_);
}
// When the settings navigation stack only has one view controller, calling
// -popViewControllerOrCloseSettingsAnimated: calls -closeSettings on the
// delegate.
TEST_F(SettingsNavigationControllerTest,
CloseSettingsWhenNavigationStackSizeIsOne) {
base::scoped_nsobject<SettingsNavigationController> settingsController(
[SettingsNavigationController
newSettingsMainControllerWithMainBrowserState:chrome_browser_state_
.get()
currentBrowserState:chrome_browser_state_
.get()
delegate:mockDelegate_]);
EXPECT_EQ(1U, [[settingsController viewControllers] count]);
[[mockDelegate_ expect] closeSettings];
[settingsController popViewControllerOrCloseSettingsAnimated:NO];
EXPECT_OCMOCK_VERIFY(mockDelegate_);
}
} // namespace
// 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_UI_SETTINGS_SETTINGS_UTILS_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_UTILS_H_
#import <UIKit/UIKit.h>
#include "ios/chrome/browser/procedural_block_types.h"
namespace ios_internal_settings {
// Returns a ProceduralBlockWithURL that sends a chrome command up the
// |responder|'s responder chain that dismisses settings and opens url
// (parameter to the block) in a new tab.
ProceduralBlockWithURL BlockToOpenURL(UIResponder* responder);
} // namespace ios_internal_settings
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_UTILS_H_
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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