Commit 956596ea authored by Stepan Khapugin's avatar Stepan Khapugin Committed by Commit Bot

[Getaway] Create incognito reauth scene agent.

Create a new agent that tracks authentication status for incognito
content in a given scene. Registers a pref for this setting.

Bug: 1138887
Change-Id: If5c094a645d41f744dd6f7f9d7dfda703f5b286d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2522084
Commit-Queue: Stepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Reviewed-by: default avatarRobbie Gibson <rkgibson@google.com>
Cr-Commit-Position: refs/heads/master@{#827241}
parent 2c398c33
......@@ -145,4 +145,8 @@ const char kIosUserZoomMultipliers[] = "ios.user_zoom_multipliers";
const char kPrintingEnabled[] = "printing.enabled";
// Bool used for the incognito biometric authentication setting.
const char kIncognitoAuthenticationSetting[] =
"ios.settings.incognito_authentication_enabled";
} // namespace prefs
......@@ -47,6 +47,8 @@ extern const char kOmniboxGeolocationLastAuthorizationAlertVersion[];
extern const char kIosUserZoomMultipliers[];
extern const char kIncognitoAuthenticationSetting[];
} // namespace prefs
#endif // IOS_CHROME_BROWSER_PREF_NAMES_H_
......@@ -84,6 +84,7 @@ source_set("browser_prefs") {
"//ios/chrome/browser/ui/bookmarks:constants",
"//ios/chrome/browser/ui/bookmarks:core",
"//ios/chrome/browser/ui/first_run:field_trial",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
"//ios/chrome/browser/voice:prefs",
"//ios/chrome/browser/web",
"//ios/public/provider/chrome/browser",
......
......@@ -67,6 +67,7 @@
#import "ios/chrome/browser/ui/bookmarks/bookmark_path_cache.h"
#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
#import "ios/chrome/browser/ui/first_run/location_permissions_field_trial.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#include "ios/chrome/browser/voice/voice_search_prefs_registration.h"
#import "ios/chrome/browser/web/font_size_tab_helper.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
......@@ -126,6 +127,7 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
[OmniboxGeolocationLocalState registerLocalState:registry];
[MemoryDebuggerManager registerLocalState:registry];
[IncognitoReauthSceneAgent registerLocalState:registry];
registry->RegisterBooleanPref(prefs::kBrowsingDataMigrationHasBeenPossible,
false);
......
# Copyright 2020 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/build/chrome_build.gni")
source_set("incognito_reauth_scene_agent") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"incognito_reauth_scene_agent.h",
"incognito_reauth_scene_agent.mm",
]
deps = [
"//base",
"//components/pref_registry",
"//components/prefs",
"//ios/chrome/browser",
"//ios/chrome/browser:pref_names",
"//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/main:browser_interface_provider",
"//ios/chrome/browser/ui/main:scene_state_header",
"//ios/chrome/browser/ui/util:multiwindow_util",
"//ios/chrome/browser/web_state_list",
]
frameworks = [ "UIKit.framework" ]
}
source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [ "incognito_reauth_scene_agent_unittest.mm" ]
deps = [
":incognito_reauth_scene_agent",
"//base",
"//base/test:test_support",
"//components/prefs:test_support",
"//ios/chrome/browser:pref_names",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/main:test_support",
"//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/browser_view",
"//ios/chrome/browser/ui/main:browser_interface_provider",
"//ios/chrome/browser/ui/main/test",
"//ios/chrome/browser/web_state_list",
"//ios/chrome/browser/web_state_list:test_support",
"//ios/chrome/browser/web_state_list:web_state_list",
"//ios/chrome/test:block_cleanup_test",
"//ios/web/public/test",
"//testing/gtest",
"//third_party/ocmock",
]
}
// Copyright 2020 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_INCOGNITO_REAUTH_INCOGNITO_REAUTH_SCENE_AGENT_H_
#define IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_SCENE_AGENT_H_
#import "ios/chrome/browser/ui/main/scene_state.h"
class PrefRegistrySimple;
class PrefService;
// A scene agent that tracks the incognito authentication status for the current
// scene.
@interface IncognitoReauthSceneAgent : NSObject <SceneAgent>
// Registers the prefs required for this agent.
+ (void)registerLocalState:(PrefRegistrySimple*)registry;
// Returns YES when the authentication is currently required.
@property(nonatomic, assign, readonly, getter=isAuthenticationRequired)
BOOL authenticationRequired;
// Local state pref service used by this object. Will default to the one from
// ApplicationContext, but is settable for overriding.
@property(nonatomic, assign) PrefService* localState;
// Requests authentication and marks this scene as authenticated until the next
// scene foregrounding. The callback will receive the result of the
// authentication attempt. It will be called on main thread, asynchronously.
- (void)authenticateWithCompletion:(void (^)(BOOL))completion;
@end
#endif // IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_SCENE_AGENT_H_
// Copyright 2020 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/incognito_reauth/incognito_reauth_scene_agent.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "ios/chrome/browser/application_context.h"
#import "ios/chrome/browser/main/browser.h"
#include "ios/chrome/browser/pref_names.h"
#import "ios/chrome/browser/ui/main/browser_interface_provider.h"
#import "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface IncognitoReauthSceneAgent () <SceneStateObserver>
// Scene state this agent serves.
@property(nonatomic, weak) SceneState* sceneState;
// Set when the scene goes foreground. Checks if any incognito tabs were open.
@property(nonatomic, assign) BOOL windowHadIncognitoContentOnForeground;
// Tracks wether the user authenticated for incognito since last launch.
@property(nonatomic, assign) BOOL authenticatedSinceLastForeground;
@end
@implementation IncognitoReauthSceneAgent
#pragma mark - class public
+ (void)registerLocalState:(PrefRegistrySimple*)registry {
registry->RegisterBooleanPref(prefs::kIncognitoAuthenticationSetting, false);
}
#pragma mark - public
- (BOOL)isAuthenticationRequired {
return base::FeatureList::IsEnabled(kIncognitoAuthentication) &&
[self authEnabledInSettings] &&
self.windowHadIncognitoContentOnForeground &&
!self.authenticatedSinceLastForeground;
}
- (void)authenticateWithCompletion:(void (^)(BOOL))completion {
// TODO: provide actual implementation.
self.authenticatedSinceLastForeground = YES;
if (completion) {
completion(YES);
}
}
#pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
if (level <= SceneActivationLevelBackground) {
self.authenticatedSinceLastForeground = NO;
}
if (level >= SceneActivationLevelForegroundInactive) {
if (sceneState.interfaceProvider.hasIncognitoInterface) {
self.windowHadIncognitoContentOnForeground =
sceneState.interfaceProvider.incognitoInterface.browser
->GetWebStateList()
->count() > 0;
} else {
self.windowHadIncognitoContentOnForeground = NO;
}
}
}
#pragma mark - SceneAgent
- (void)setSceneState:(SceneState*)sceneState {
DCHECK(!_sceneState);
_sceneState = sceneState;
[sceneState addObserver:self];
}
#pragma mark - private
- (PrefService*)localState {
if (!_localState) {
_localState = GetApplicationContext()->GetLocalState();
}
return _localState;
}
// Convenience method to check the pref associated with the reauth setting.
- (BOOL)authEnabledInSettings {
return self.localState->GetBoolean(prefs::kIncognitoAuthenticationSetting);
}
@end
// Copyright 2020 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/incognito_reauth/incognito_reauth_scene_agent.h"
#include "base/feature_list.h"
#include "base/test/scoped_feature_list.h"
#include "components/prefs/testing_pref_service.h"
#import "ios/chrome/browser/main/test_browser.h"
#include "ios/chrome/browser/pref_names.h"
#import "ios/chrome/browser/ui/main/browser_interface_provider.h"
#import "ios/chrome/browser/ui/main/test/stub_browser_interface_provider.h"
#import "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/chrome/browser/web_state_list/web_state_opener.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
class IncognitoReauthSceneAgentTest : public PlatformTest {
public:
IncognitoReauthSceneAgentTest()
: scene_state_([[SceneState alloc] initWithAppState:nil]),
scene_state_mock_(OCMPartialMock(scene_state_)),
agent_([[IncognitoReauthSceneAgent alloc] init]) {
[scene_state_ addAgent:agent_];
}
protected:
// Returns a WebStateList with |tabs_count| WebStates.
std::unique_ptr<WebStateList> CreateWebStateList(int tabs_count) {
std::unique_ptr<WebStateList> web_state_list =
std::make_unique<WebStateList>(&web_state_list_delegate_);
for (int i = 0; i < tabs_count; ++i) {
web_state_list->InsertWebState(i, std::make_unique<web::TestWebState>(),
WebStateList::INSERT_FORCE_INDEX,
WebStateOpener());
}
return web_state_list;
}
void SetUpTestObjects(int tab_count, bool enable_flag, bool enable_pref) {
// Mock the feature flag.
feature_list_.InitWithFeatureState(kIncognitoAuthentication, enable_flag);
// Stub all calls to be able to mock the following:
// 1. sceneState.interfaceProvider.incognitoInterface
// .browser->GetWebStateList()->count()
// 2. sceneState.interfaceProvider.hasIncognitoInterface
web_state_list_ = CreateWebStateList(tab_count);
test_browser_ = std::make_unique<TestBrowser>(/*BrowserState=*/nullptr,
web_state_list_.get());
stub_browser_interface_provider_ =
[[StubBrowserInterfaceProvider alloc] init];
stub_browser_interface_provider_.incognitoInterface.browser =
test_browser_.get();
OCMStub([scene_state_mock_ interfaceProvider])
.andReturn(stub_browser_interface_provider_);
[IncognitoReauthSceneAgent registerLocalState:pref_service_.registry()];
agent_.localState = &pref_service_;
pref_service_.SetBoolean(prefs::kIncognitoAuthenticationSetting,
enable_pref);
}
// The scene state that the agent works with.
SceneState* scene_state_;
// Partial mock for stubbing scene_state_'s methods
id scene_state_mock_;
// The tested agent
IncognitoReauthSceneAgent* agent_;
StubBrowserInterfaceProvider* stub_browser_interface_provider_;
std::unique_ptr<TestBrowser> test_browser_;
std::unique_ptr<WebStateList> web_state_list_;
TestingPrefServiceSimple pref_service_;
base::test::ScopedFeatureList feature_list_;
FakeWebStateListDelegate web_state_list_delegate_;
};
// TODO: add test that after successful auth, auth is not required
// TODO: add test that after unsuccessful auth, auth is required
// Test that when the feature pref is disabled, auth isn't required.
TEST_F(IncognitoReauthSceneAgentTest, PrefDisabled) {
SetUpTestObjects(/*tab_count=*/1, /*enable_flag=*/true,
/*enable_pref=*/false);
// Go foreground.
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_FALSE(agent_.authenticationRequired);
}
// Test that when the feature flag is disabled, auth isn't required.
TEST_F(IncognitoReauthSceneAgentTest, FlagDisabled) {
SetUpTestObjects(/*tab_count=*/1, /*enable_flag=*/false,
/*enable_pref=*/true);
// Go foreground.
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_FALSE(agent_.authenticationRequired);
}
// Test that when the feature is enabled, we're foregrounded with some incognito
// content already present, auth is required
TEST_F(IncognitoReauthSceneAgentTest, NeedsAuth) {
SetUpTestObjects(/*tab_count=*/1, /*enable_flag=*/true, /*enable_pref=*/true);
// Go foreground.
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_TRUE(agent_.authenticationRequired);
}
// Test that when auth is required and is successfully performed, it's not
// required anymore.
TEST_F(IncognitoReauthSceneAgentTest, SuccessfulAuth) {
SetUpTestObjects(/*tab_count=*/1, /*enable_flag=*/true, /*enable_pref=*/true);
// Go foreground.
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_TRUE(agent_.authenticationRequired);
[agent_ authenticateWithCompletion:^(BOOL result) {
EXPECT_TRUE(result);
}];
// Auth not required
EXPECT_FALSE(agent_.authenticationRequired);
// Auth required after backgrounding.
scene_state_.activationLevel = SceneActivationLevelBackground;
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_TRUE(agent_.authenticationRequired);
}
// Test that when the feature is enabled, auth isn't required if we foreground
// without any incognito tabs.
TEST_F(IncognitoReauthSceneAgentTest, AuthNotRequiredWhenNoIncognitoTabs) {
SetUpTestObjects(/*tab_count=*/0, /*enable_flag=*/true, /*enable_pref=*/true);
// Go foreground.
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_FALSE(agent_.authenticationRequired);
}
// Test that when the feature is enabled, we're foregrounded with some incognito
// content already present, auth is required
TEST_F(IncognitoReauthSceneAgentTest,
AuthNotRequiredWhenNoIncognitoTabsOnForeground) {
SetUpTestObjects(/*tab_count=*/0, /*enable_flag=*/true, /*enable_pref=*/true);
// Go foreground.
scene_state_.activationLevel = SceneActivationLevelForegroundActive;
EXPECT_FALSE(agent_.authenticationRequired);
// Open another tab.
web_state_list_->InsertWebState(0, std::make_unique<web::TestWebState>(),
WebStateList::INSERT_FORCE_INDEX,
WebStateOpener());
EXPECT_FALSE(agent_.authenticationRequired);
}
} // namespace
......@@ -104,6 +104,7 @@ source_set("scene") {
"//ios/chrome/browser/ui/first_run",
"//ios/chrome/browser/ui/first_run:field_trial",
"//ios/chrome/browser/ui/history",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
"//ios/chrome/browser/ui/settings:settings_root",
"//ios/chrome/browser/ui/settings/sync",
"//ios/chrome/browser/ui/tab_switcher/tab_grid",
......
......@@ -69,6 +69,8 @@ class ChromeBrowserState;
@property(nonatomic, readonly) id<BrowserInterface> mainInterface;
// The incognito interface. Its |incognito| property must be YES.
@property(nonatomic, readonly) id<BrowserInterface> incognitoInterface;
// YES iff |incognitoInterface| is already created.
@property(nonatomic, assign, readonly) BOOL hasIncognitoInterface;
@end
......
......@@ -252,6 +252,10 @@
return _incognitoInterface;
}
- (BOOL)hasIncognitoInterface {
return _incognitoInterface;
}
- (Browser*)mainBrowser {
DCHECK(_mainBrowser.get())
<< "-createMainBrowser must be called before -mainBrowser is accessed.";
......
......@@ -73,6 +73,7 @@
#import "ios/chrome/browser/ui/first_run/orientation_limiting_navigation_controller.h"
#import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
#include "ios/chrome/browser/ui/history/history_coordinator.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/ui/main/browser_interface_provider.h"
#import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
#import "ios/chrome/browser/ui/main/default_browser_scene_agent.h"
......@@ -259,6 +260,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Add agents.
[_sceneState addAgent:[[UIBlockerSceneAgent alloc] init]];
[_sceneState addAgent:[[IncognitoBlockerSceneAgent alloc] init]];
[_sceneState addAgent:[[IncognitoReauthSceneAgent alloc] init]];
}
return self;
}
......
......@@ -21,4 +21,11 @@
}
return self;
}
#pragma mark - BrowserInterfaceProvider
- (BOOL)hasIncognitoInterface {
return _incognitoInterface;
}
@end
......@@ -264,6 +264,7 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/ui/gestures:unit_tests",
"//ios/chrome/browser/ui/history:unit_tests",
"//ios/chrome/browser/ui/icons:unit_tests",
"//ios/chrome/browser/ui/incognito_reauth:unit_tests",
"//ios/chrome/browser/ui/infobars:unit_tests",
"//ios/chrome/browser/ui/infobars/banners:unit_tests",
"//ios/chrome/browser/ui/keyboard:unit_tests",
......
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