Commit eca462b8 authored by Stepan Khapugin's avatar Stepan Khapugin Committed by Commit Bot

[iOS][Getaway] Implement data plumbing to tab switcher UI.

Adds necessary observation code and plumbing to the tab switcher view
controller to add UI in the future.

Bug: none
Change-Id: I5912042ecf4634b7a8c0b3628b40505293cfcdaa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2536454
Commit-Queue: Stepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#830672}
parent 881b5838
...@@ -4,6 +4,19 @@ ...@@ -4,6 +4,19 @@
import("//ios/build/chrome_build.gni") import("//ios/build/chrome_build.gni")
source_set("incognito_reauth_ui") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"incognito_reauth_view.h",
"incognito_reauth_view.mm",
]
deps = [ "//ios/chrome/common/ui/util" ]
frameworks = [
"UIKit.framework",
"LocalAuthentication.framework",
]
}
source_set("incognito_reauth_scene_agent") { source_set("incognito_reauth_scene_agent") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
......
...@@ -7,10 +7,21 @@ ...@@ -7,10 +7,21 @@
#import "ios/chrome/browser/ui/main/scene_state.h" #import "ios/chrome/browser/ui/main/scene_state.h"
@class IncognitoReauthSceneAgent;
class PrefRegistrySimple; class PrefRegistrySimple;
class PrefService; class PrefService;
@protocol ReauthenticationProtocol; @protocol ReauthenticationProtocol;
@protocol IncognitoReauthObserver <NSObject>
@optional
// Called when the authentication requirement in a given scene might have
// changed.
- (void)reauthAgent:(IncognitoReauthSceneAgent*)agent
didUpdateAuthenticationRequirement:(BOOL)isRequired;
@end
// A scene agent that tracks the incognito authentication status for the current // A scene agent that tracks the incognito authentication status for the current
// scene. // scene.
@interface IncognitoReauthSceneAgent : NSObject <SceneAgent> @interface IncognitoReauthSceneAgent : NSObject <SceneAgent>
...@@ -42,6 +53,11 @@ class PrefService; ...@@ -42,6 +53,11 @@ class PrefService;
// authentication attempt. It will be called on main thread, asynchronously. // authentication attempt. It will be called on main thread, asynchronously.
- (void)authenticateWithCompletion:(void (^)(BOOL))completion; - (void)authenticateWithCompletion:(void (^)(BOOL))completion;
#pragma mark observation
- (void)addObserver:(id<IncognitoReauthObserver>)observer;
- (void)removeObserver:(id<IncognitoReauthObserver>)observer;
@end @end
#endif // IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_SCENE_AGENT_H_ #endif // IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_SCENE_AGENT_H_
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h" #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#include "base/check.h" #include "base/check.h"
#import "base/ios/crb_protocol_observers.h"
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "ios/chrome/browser/application_context.h" #include "ios/chrome/browser/application_context.h"
...@@ -19,6 +20,14 @@ ...@@ -19,6 +20,14 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
@interface IncognitoReauthObserverList
: CRBProtocolObservers <IncognitoReauthObserver>
@end
@implementation IncognitoReauthObserverList
@end
#pragma mark - IncognitoReauthSceneAgent
@interface IncognitoReauthSceneAgent () <SceneStateObserver> @interface IncognitoReauthSceneAgent () <SceneStateObserver>
// Scene state this agent serves. // Scene state this agent serves.
...@@ -30,6 +39,9 @@ ...@@ -30,6 +39,9 @@
// Tracks wether the user authenticated for incognito since last launch. // Tracks wether the user authenticated for incognito since last launch.
@property(nonatomic, assign) BOOL authenticatedSinceLastForeground; @property(nonatomic, assign) BOOL authenticatedSinceLastForeground;
// Container for observers.
@property(nonatomic, strong) IncognitoReauthObserverList* observers;
@end @end
@implementation IncognitoReauthSceneAgent @implementation IncognitoReauthSceneAgent
...@@ -48,14 +60,14 @@ ...@@ -48,14 +60,14 @@
if (self) { if (self) {
DCHECK(reauthModule); DCHECK(reauthModule);
_reauthModule = reauthModule; _reauthModule = reauthModule;
_observers = [IncognitoReauthObserverList
observersWithProtocol:@protocol(IncognitoReauthObserver)];
} }
return self; return self;
} }
- (BOOL)isAuthenticationRequired { - (BOOL)isAuthenticationRequired {
return base::FeatureList::IsEnabled(kIncognitoAuthentication) && return [self featureEnabled] && self.windowHadIncognitoContentOnForeground &&
[self authEnabledInSettings] &&
self.windowHadIncognitoContentOnForeground &&
!self.authenticatedSinceLastForeground; !self.authenticatedSinceLastForeground;
} }
...@@ -87,6 +99,36 @@ ...@@ -87,6 +99,36 @@
}]; }];
} }
- (void)addObserver:(id<IncognitoReauthObserver>)observer {
[self.observers addObserver:observer];
}
- (void)removeObserver:(id<IncognitoReauthObserver>)observer {
[self.observers removeObserver:observer];
}
#pragma mark properties
- (void)setAuthenticatedSinceLastForeground:(BOOL)authenticated {
_authenticatedSinceLastForeground = authenticated;
if (self.featureEnabled) {
[self notifyObservers];
}
}
- (void)setWindowHadIncognitoContentOnForeground:(BOOL)hadIncognitoContent {
_windowHadIncognitoContentOnForeground = hadIncognitoContent;
if (self.featureEnabled) {
[self notifyObservers];
}
}
- (void)notifyObservers {
DCHECK(self.featureEnabled);
[self.observers reauthAgent:self
didUpdateAuthenticationRequirement:self.isAuthenticationRequired];
}
#pragma mark - SceneStateObserver #pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState - (void)sceneState:(SceneState*)sceneState
...@@ -119,14 +161,21 @@ ...@@ -119,14 +161,21 @@
- (PrefService*)localState { - (PrefService*)localState {
if (!_localState) { if (!_localState) {
if (!GetApplicationContext()) {
// This is called before application context was initialized.
return nil;
}
_localState = GetApplicationContext()->GetLocalState(); _localState = GetApplicationContext()->GetLocalState();
} }
return _localState; return _localState;
} }
// Convenience method to check the pref associated with the reauth setting. // Convenience method to check the pref associated with the reauth setting and
- (BOOL)authEnabledInSettings { // the feature flag.
return self.localState->GetBoolean(prefs::kIncognitoAuthenticationSetting); - (BOOL)featureEnabled {
return base::FeatureList::IsEnabled(kIncognitoAuthentication) &&
self.localState &&
self.localState->GetBoolean(prefs::kIncognitoAuthenticationSetting);
} }
@end @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.
#ifndef IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_VIEW_H_
#define IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_VIEW_H_
#import <UIKit/UIKit.h>
// The view that is used to overlay over non-authorized incognito content.
@interface IncognitoReauthView : UIView
// Button that allows biometric authentication.
// Will auto-adjust its string based on the available authentication methods on
// the user's device.
@property(nonatomic, strong, readonly) UIButton* authenticateButton;
// The button to go to the tab switcher.
@property(nonatomic, strong, readonly) UIButton* tabSwitcherButton;
@end
#endif // IOS_CHROME_BROWSER_UI_INCOGNITO_REAUTH_INCOGNITO_REAUTH_VIEW_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_view.h"
#import <LocalAuthentication/LocalAuthentication.h>
#import "ios/chrome/common/ui/util/constraints_ui_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
const CGFloat kButtonHeight = 60.0f;
const CGFloat kButtonWidth = 190.0f;
const CGFloat kButtonSpacing = 16.0f;
} // namespace
@implementation IncognitoReauthView
- (instancetype)init {
self = [super init];
if (self) {
UIBlurEffect* blurEffect =
[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
UIVisualEffectView* blurBackgroundView =
[[UIVisualEffectView alloc] initWithEffect:blurEffect];
[self addSubview:blurBackgroundView];
blurBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
AddSameConstraints(self, blurBackgroundView);
_authenticateButton =
[IncognitoReauthView newRoundButtonWithBlurEffect:blurEffect];
[_authenticateButton
setTitle:[IncognitoReauthView authenticationActionLabel]
forState:UIControlStateNormal];
_tabSwitcherButton =
[IncognitoReauthView newRoundButtonWithBlurEffect:blurEffect];
// TODO(crbug.com/1138892): add localized text.
[_tabSwitcherButton setTitle:@"[Test String] Go to Tab Switcher"
forState:UIControlStateNormal];
UIStackView* stackView = [[UIStackView alloc]
initWithArrangedSubviews:@[ _authenticateButton, _tabSwitcherButton ]];
stackView.axis = UILayoutConstraintAxisVertical;
stackView.translatesAutoresizingMaskIntoConstraints = NO;
stackView.spacing = kButtonSpacing;
[self addSubview:stackView];
AddSameCenterConstraints(blurBackgroundView, stackView);
}
return self;
}
+ (NSString*)authenticationActionLabel {
LAContext* ctx = [[LAContext alloc] init];
switch (ctx.biometryType) {
case LABiometryTypeFaceID:
return @"Face ID";
case LABiometryTypeTouchID:
return @"Touch ID";
default:
// TODO(crbug.com/1138892): add localized text.
return @"[Test String] Passcode";
}
}
+ (UIButton*)newRoundButtonWithBlurEffect:(UIBlurEffect*)blurEffect {
UIButton* button = [[UIButton alloc] init];
button.backgroundColor = [UIColor clearColor];
[NSLayoutConstraint activateConstraints:@[
[button.heightAnchor constraintEqualToConstant:kButtonHeight],
[button.widthAnchor constraintEqualToConstant:kButtonWidth],
]];
UIView* backgroundView = nil;
if (@available(iOS 13, *)) {
backgroundView = [[UIVisualEffectView alloc]
initWithEffect:[UIVibrancyEffect
effectForBlurEffect:blurEffect
style:UIVibrancyEffectStyleFill]];
} else {
backgroundView = [[UIView alloc] init];
}
backgroundView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.2];
backgroundView.layer.cornerRadius = kButtonHeight / 2;
backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
[button addSubview:backgroundView];
AddSameConstraints(backgroundView, button);
return button;
}
@end
...@@ -273,13 +273,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -273,13 +273,11 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Add agents. // Add agents.
[_sceneState addAgent:[[UIBlockerSceneAgent alloc] init]]; [_sceneState addAgent:[[UIBlockerSceneAgent alloc] init]];
[_sceneState addAgent:[[IncognitoBlockerSceneAgent alloc] init]]; [_sceneState addAgent:[[IncognitoBlockerSceneAgent alloc] init]];
if (base::FeatureList::IsEnabled(kIncognitoAuthentication)) {
[_sceneState [_sceneState
addAgent:[[IncognitoReauthSceneAgent alloc] addAgent:[[IncognitoReauthSceneAgent alloc]
initWithReauthModule:[[ReauthenticationModule alloc] initWithReauthModule:[[ReauthenticationModule alloc]
init]]]; init]]];
} }
}
return self; return self;
} }
......
...@@ -41,6 +41,7 @@ source_set("tab_grid") { ...@@ -41,6 +41,7 @@ source_set("tab_grid") {
"//ios/chrome/browser/ui/gestures", "//ios/chrome/browser/ui/gestures",
"//ios/chrome/browser/ui/history", "//ios/chrome/browser/ui/history",
"//ios/chrome/browser/ui/history/public", "//ios/chrome/browser/ui/history/public",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
"//ios/chrome/browser/ui/main", "//ios/chrome/browser/ui/main",
"//ios/chrome/browser/ui/recent_tabs", "//ios/chrome/browser/ui/recent_tabs",
"//ios/chrome/browser/ui/recent_tabs:recent_tabs_ui", "//ios/chrome/browser/ui/recent_tabs:recent_tabs_ui",
...@@ -164,6 +165,7 @@ source_set("unit_tests") { ...@@ -164,6 +165,7 @@ source_set("unit_tests") {
"//ios/chrome/browser/tabs", "//ios/chrome/browser/tabs",
"//ios/chrome/browser/tabs:tabs_internal", "//ios/chrome/browser/tabs:tabs_internal",
"//ios/chrome/browser/ui/commands", "//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/main:scene_state_header",
"//ios/chrome/browser/ui/tab_switcher", "//ios/chrome/browser/ui/tab_switcher",
"//ios/chrome/browser/web", "//ios/chrome/browser/web",
"//ios/chrome/browser/web:page_placeholder", "//ios/chrome/browser/web:page_placeholder",
......
...@@ -53,6 +53,7 @@ source_set("grid_ui") { ...@@ -53,6 +53,7 @@ source_set("grid_ui") {
"//ios/chrome/browser/ui:feature_flags", "//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/elements", "//ios/chrome/browser/ui/elements",
"//ios/chrome/browser/ui/gestures", "//ios/chrome/browser/ui/gestures",
"//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_ui",
"//ios/chrome/browser/ui/tab_switcher", "//ios/chrome/browser/ui/tab_switcher",
"//ios/chrome/browser/ui/tab_switcher/tab_grid/transitions", "//ios/chrome/browser/ui/tab_switcher/tab_grid/transitions",
"//ios/chrome/browser/ui/thumb_strip:feature_flags", "//ios/chrome/browser/ui/thumb_strip:feature_flags",
......
...@@ -12,6 +12,10 @@ ...@@ -12,6 +12,10 @@
// Supports idempotent insert/delete/updates to a grid. // Supports idempotent insert/delete/updates to a grid.
@protocol GridConsumer @protocol GridConsumer
// Notify consumer that the displayed items require authentication before they
// can be accessed. Used for biometric incognito tab authentication.
- (void)setItemsRequireAuthentication:(BOOL)requireAuthentication;
// Many of the following methods pass a |selectedItemID| as a parameter, // Many of the following methods pass a |selectedItemID| as a parameter,
// indicating the identifier of the item that should be in the selected state // indicating the identifier of the item that should be in the selected state
// after the method is called. In every such case, a nil |selectedItemID| // after the method is called. In every such case, a nil |selectedItemID|
......
...@@ -45,6 +45,11 @@ ...@@ -45,6 +45,11 @@
- (void)didChangeLastItemVisibilityInGridViewController: - (void)didChangeLastItemVisibilityInGridViewController:
(GridViewController*)gridViewController; (GridViewController*)gridViewController;
// Tells the delegate when the currently displayed content is hidden from the
// user until they authenticate. Used for incognito biometric authentication.
- (void)gridViewController:(GridViewController*)gridViewController
contentNeedsAuthenticationChanged:(BOOL)needsAuth;
@end @end
// A view controller that contains a grid of items. // A view controller that contains a grid of items.
...@@ -71,6 +76,9 @@ ...@@ -71,6 +76,9 @@
@property(nonatomic, assign) BOOL showsSelectionUpdates; @property(nonatomic, assign) BOOL showsSelectionUpdates;
// The fraction of the last item of the grid that is visible. // The fraction of the last item of the grid that is visible.
@property(nonatomic, assign, readonly) CGFloat fractionVisibleOfLastItem; @property(nonatomic, assign, readonly) CGFloat fractionVisibleOfLastItem;
// YES when the current contents are hidden from the user before a successful
// biometric authentication.
@property(nonatomic, assign) BOOL contentNeedsAuthentication;
// Returns the layout of the grid for use in an animated transition. // Returns the layout of the grid for use in an animated transition.
- (GridTransitionLayout*)transitionLayout; - (GridTransitionLayout*)transitionLayout;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#import "base/numerics/safe_conversions.h" #import "base/numerics/safe_conversions.h"
#include "ios/chrome/browser/drag_and_drop/drag_and_drop_flag.h" #include "ios/chrome/browser/drag_and_drop/drag_and_drop_flag.h"
#include "ios/chrome/browser/procedural_block_types.h" #include "ios/chrome/browser/procedural_block_types.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_view.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h" #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h" #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_drag_drop_handler.h" #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_drag_drop_handler.h"
...@@ -56,6 +57,9 @@ NSIndexPath* CreateIndexPath(NSInteger index) { ...@@ -56,6 +57,9 @@ NSIndexPath* CreateIndexPath(NSInteger index) {
UICollectionViewDropDelegate> UICollectionViewDropDelegate>
// A collection view of items in a grid format. // A collection view of items in a grid format.
@property(nonatomic, weak) UICollectionView* collectionView; @property(nonatomic, weak) UICollectionView* collectionView;
// A view to obscure incognito content when the user isn't authorized to
// see it.
@property(nonatomic, strong) IncognitoReauthView* blockingView;
// The local model backing the collection view. // The local model backing the collection view.
@property(nonatomic, strong) NSMutableArray<TabSwitcherItem*>* items; @property(nonatomic, strong) NSMutableArray<TabSwitcherItem*>* items;
// Identifier of the selected item. This value is disregarded if |self.items| is // Identifier of the selected item. This value is disregarded if |self.items| is
...@@ -614,6 +618,27 @@ NSIndexPath* CreateIndexPath(NSInteger index) { ...@@ -614,6 +618,27 @@ NSIndexPath* CreateIndexPath(NSInteger index) {
#pragma mark - GridConsumer #pragma mark - GridConsumer
- (void)setItemsRequireAuthentication:(BOOL)require {
self.contentNeedsAuthentication = require;
[self.delegate gridViewController:self
contentNeedsAuthenticationChanged:require];
if (require) {
if (!self.blockingView) {
self.blockingView = [[IncognitoReauthView alloc] init];
self.blockingView.translatesAutoresizingMaskIntoConstraints = NO;
self.blockingView.layer.zPosition = FLT_MAX;
// No need to show tab switcher button when already in the tab switcher.
self.blockingView.tabSwitcherButton.hidden = YES;
}
[self.view addSubview:self.blockingView];
AddSameConstraints(self.collectionView.frameLayoutGuide, self.blockingView);
} else {
[self.blockingView removeFromSuperview];
}
}
- (void)populateItems:(NSArray<TabSwitcherItem*>*)items - (void)populateItems:(NSArray<TabSwitcherItem*>*)items
selectedItemID:(NSString*)selectedItemID { selectedItemID:(NSString*)selectedItemID {
#ifndef NDEBUG #ifndef NDEBUG
......
...@@ -31,6 +31,10 @@ ...@@ -31,6 +31,10 @@
@end @end
@implementation FakeGridViewControllerDelegate @implementation FakeGridViewControllerDelegate
@synthesize itemCount = _itemCount; @synthesize itemCount = _itemCount;
- (void)gridViewController:(GridViewController*)gridViewController
contentNeedsAuthenticationChanged:(BOOL)needsAuth {
}
- (void)gridViewController:(GridViewController*)gridViewController - (void)gridViewController:(GridViewController*)gridViewController
didChangeItemCount:(NSUInteger)count { didChangeItemCount:(NSUInteger)count {
self.itemCount = count; self.itemCount = count;
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
// Sets target/action for tapping event on new tab button. // Sets target/action for tapping event on new tab button.
- (void)setNewTabButtonTarget:(id)target action:(SEL)action; - (void)setNewTabButtonTarget:(id)target action:(SEL)action;
// Set |enabled| on the new tab button.
- (void)setNewTabButtonEnabled:(BOOL)enabled;
// Hides components and uses a black background color for tab grid transition // Hides components and uses a black background color for tab grid transition
// animation. // animation.
......
...@@ -86,6 +86,11 @@ ...@@ -86,6 +86,11 @@
forControlEvents:UIControlEventTouchUpInside]; forControlEvents:UIControlEventTouchUpInside];
} }
- (void)setNewTabButtonEnabled:(BOOL)enabled {
_smallNewTabButton.enabled = enabled;
_largeNewTabButton.enabled = enabled;
}
- (void)hide { - (void)hide {
_smallNewTabButton.alpha = 0.0; _smallNewTabButton.alpha = 0.0;
_largeNewTabButton.alpha = 0.0; _largeNewTabButton.alpha = 0.0;
......
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
#import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h" #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
#import "ios/chrome/browser/ui/history/history_coordinator.h" #import "ios/chrome/browser/ui/history/history_coordinator.h"
#import "ios/chrome/browser/ui/history/public/history_presentation_delegate.h" #import "ios/chrome/browser/ui/history/public/history_presentation_delegate.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/ui/main/bvc_container_view_controller.h" #import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_menu_helper.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_menu_helper.h"
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_presentation_delegate.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_presentation_delegate.h"
...@@ -309,7 +311,8 @@ ...@@ -309,7 +311,8 @@
_baseViewController = baseViewController; _baseViewController = baseViewController;
self.regularTabsMediator = [[TabGridMediator alloc] self.regularTabsMediator = [[TabGridMediator alloc]
initWithConsumer:baseViewController.regularTabsConsumer]; initWithConsumer:baseViewController.regularTabsConsumer
reauthAgent:nil];
ChromeBrowserState* regularBrowserState = ChromeBrowserState* regularBrowserState =
_regularBrowser ? _regularBrowser->GetBrowserState() : nullptr; _regularBrowser ? _regularBrowser->GetBrowserState() : nullptr;
WebStateList* regularWebStateList = WebStateList* regularWebStateList =
...@@ -322,8 +325,19 @@ ...@@ -322,8 +325,19 @@
IOSChromeTabRestoreServiceFactory::GetForBrowserState( IOSChromeTabRestoreServiceFactory::GetForBrowserState(
regularBrowserState); regularBrowserState);
} }
IncognitoReauthSceneAgent* reauthAgent = nil;
for (id agent in SceneStateBrowserAgent::FromBrowser(_incognitoBrowser)
->GetSceneState()
.connectedAgents) {
if ([agent isKindOfClass:[IncognitoReauthSceneAgent class]]) {
reauthAgent = agent;
}
}
self.incognitoTabsMediator = [[TabGridMediator alloc] self.incognitoTabsMediator = [[TabGridMediator alloc]
initWithConsumer:baseViewController.incognitoTabsConsumer]; initWithConsumer:baseViewController.incognitoTabsConsumer
reauthAgent:reauthAgent];
self.incognitoTabsMediator.browser = _incognitoBrowser; self.incognitoTabsMediator.browser = _incognitoBrowser;
self.incognitoTabsMediator.delegate = self; self.incognitoTabsMediator.delegate = self;
baseViewController.regularTabsDelegate = self.regularTabsMediator; baseViewController.regularTabsDelegate = self.regularTabsMediator;
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#import "ios/chrome/browser/snapshots/snapshot_browser_agent.h" #import "ios/chrome/browser/snapshots/snapshot_browser_agent.h"
#import "ios/chrome/browser/ui/commands/application_commands.h" #import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/browsing_data_commands.h" #import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
#import "ios/chrome/browser/ui/main/scene_state.h"
#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
#include "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h" #include "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h"
#import "ios/chrome/test/block_cleanup_test.h" #import "ios/chrome/test/block_cleanup_test.h"
#include "ios/web/public/test/web_task_environment.h" #include "ios/web/public/test/web_task_environment.h"
...@@ -22,6 +24,20 @@ ...@@ -22,6 +24,20 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
@interface StubSceneState : SceneState
@end
@implementation StubSceneState {
UIWindow* _window;
}
- (void)setWindow:(UIWindow*)window {
_window = window;
}
- (UIWindow*)window {
return _window;
}
@end
@interface TestTabGridCoordinatorDelegate @interface TestTabGridCoordinatorDelegate
: NSObject <TabGridCoordinatorDelegate> : NSObject <TabGridCoordinatorDelegate>
@property(nonatomic) BOOL didEndCalled; @property(nonatomic) BOOL didEndCalled;
...@@ -42,13 +58,25 @@ ...@@ -42,13 +58,25 @@
namespace { namespace {
void AddAgentsToBrowser(Browser* browser, SceneState* scene_state) {
SnapshotBrowserAgent::CreateForBrowser(browser);
SnapshotBrowserAgent::FromBrowser(browser)->SetSessionID(
base::SysNSStringToUTF8([[NSUUID UUID] UUIDString]));
SceneStateBrowserAgent::CreateForBrowser(browser, scene_state);
}
class TabGridCoordinatorTest : public BlockCleanupTest { class TabGridCoordinatorTest : public BlockCleanupTest {
public: public:
TabGridCoordinatorTest() { TabGridCoordinatorTest() {
scene_state_ = [[StubSceneState alloc] initWithAppState:nil];
scene_state_.window =
[[UIApplication sharedApplication].windows firstObject];
browser_ = std::make_unique<TestBrowser>(); browser_ = std::make_unique<TestBrowser>();
SnapshotBrowserAgent::CreateForBrowser(browser_.get()); AddAgentsToBrowser(browser_.get(), scene_state_);
SnapshotBrowserAgent::FromBrowser(browser_.get())
->SetSessionID(base::SysNSStringToUTF8([[NSUUID UUID] UUIDString])); incognito_browser_ = std::make_unique<TestBrowser>();
AddAgentsToBrowser(incognito_browser_.get(), scene_state_);
UIWindow* window = [UIApplication sharedApplication].keyWindow; UIWindow* window = [UIApplication sharedApplication].keyWindow;
coordinator_ = [[TabGridCoordinator alloc] coordinator_ = [[TabGridCoordinator alloc]
...@@ -58,7 +86,7 @@ class TabGridCoordinatorTest : public BlockCleanupTest { ...@@ -58,7 +86,7 @@ class TabGridCoordinatorTest : public BlockCleanupTest {
browsingDataCommandEndpoint:OCMProtocolMock( browsingDataCommandEndpoint:OCMProtocolMock(
@protocol(BrowsingDataCommands)) @protocol(BrowsingDataCommands))
regularBrowser:browser_.get() regularBrowser:browser_.get()
incognitoBrowser:nil]; incognitoBrowser:incognito_browser_.get()];
coordinator_.animationsDisabledForTesting = YES; coordinator_.animationsDisabledForTesting = YES;
// TabGirdCoordinator will make its view controller the root, so stash the // TabGirdCoordinator will make its view controller the root, so stash the
// original root view controller before starting |coordinator_|. // original root view controller before starting |coordinator_|.
...@@ -93,6 +121,12 @@ class TabGridCoordinatorTest : public BlockCleanupTest { ...@@ -93,6 +121,12 @@ class TabGridCoordinatorTest : public BlockCleanupTest {
// Browser for the coordinator. // Browser for the coordinator.
std::unique_ptr<Browser> browser_; std::unique_ptr<Browser> browser_;
// Browser for the coordinator.
std::unique_ptr<Browser> incognito_browser_;
// Scene state emulated in this test.
SceneState* scene_state_;
// The TabGridCoordinator that is under test. The test fixture sets // The TabGridCoordinator that is under test. The test fixture sets
// this VC as the root VC for the window. // this VC as the root VC for the window.
TabGridCoordinator* coordinator_; TabGridCoordinator* coordinator_;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
class Browser; class Browser;
@protocol GridConsumer; @protocol GridConsumer;
@class IncognitoReauthSceneAgent;
@class TabGridMediator; @class TabGridMediator;
namespace sessions { namespace sessions {
...@@ -47,6 +48,7 @@ class TabRestoreService; ...@@ -47,6 +48,7 @@ class TabRestoreService;
// Initializer with |consumer| as the receiver of model layer updates. // Initializer with |consumer| as the receiver of model layer updates.
- (instancetype)initWithConsumer:(id<GridConsumer>)consumer - (instancetype)initWithConsumer:(id<GridConsumer>)consumer
reauthAgent:(IncognitoReauthSceneAgent*)reauthAgent
NS_DESIGNATED_INITIALIZER; NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
@end @end
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#import "ios/chrome/browser/snapshots/snapshot_tab_helper.h" #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
#include "ios/chrome/browser/system_flags.h" #include "ios/chrome/browser/system_flags.h"
#import "ios/chrome/browser/tabs/tab_title_util.h" #import "ios/chrome/browser/tabs/tab_title_util.h"
#import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_consumer.h" #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_consumer.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h" #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
#import "ios/chrome/browser/web/tab_id_tab_helper.h" #import "ios/chrome/browser/web/tab_id_tab_helper.h"
...@@ -109,6 +110,7 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list, ...@@ -109,6 +110,7 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
} // namespace } // namespace
@interface TabGridMediator () <CRWWebStateObserver, @interface TabGridMediator () <CRWWebStateObserver,
IncognitoReauthObserver,
SnapshotCacheObserver, SnapshotCacheObserver,
WebStateListObserving> WebStateListObserving>
// The list from the browser. // The list from the browser.
...@@ -125,6 +127,8 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list, ...@@ -125,6 +127,8 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
// Short-term cache for grid thumbnails. // Short-term cache for grid thumbnails.
@property(nonatomic, strong) @property(nonatomic, strong)
NSMutableDictionary<NSString*, UIImage*>* appearanceCache; NSMutableDictionary<NSString*, UIImage*>* appearanceCache;
// Agent tracking the authentication status.
@property(nonatomic, weak) IncognitoReauthSceneAgent* reauthAgent;
@end @end
@implementation TabGridMediator { @implementation TabGridMediator {
...@@ -138,7 +142,8 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list, ...@@ -138,7 +142,8 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
_scopedWebStateObserver; _scopedWebStateObserver;
} }
- (instancetype)initWithConsumer:(id<GridConsumer>)consumer { - (instancetype)initWithConsumer:(id<GridConsumer>)consumer
reauthAgent:(IncognitoReauthSceneAgent*)agent {
if (self = [super init]) { if (self = [super init]) {
_consumer = consumer; _consumer = consumer;
_webStateListObserverBridge = _webStateListObserverBridge =
...@@ -152,6 +157,8 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list, ...@@ -152,6 +157,8 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
std::make_unique<ScopedObserver<web::WebState, web::WebStateObserver>>( std::make_unique<ScopedObserver<web::WebState, web::WebStateObserver>>(
_webStateObserverBridge.get()); _webStateObserverBridge.get());
_appearanceCache = [[NSMutableDictionary alloc] init]; _appearanceCache = [[NSMutableDictionary alloc] init];
_reauthAgent = agent;
[agent addObserver:self];
} }
return self; return self;
} }
...@@ -557,6 +564,13 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list, ...@@ -557,6 +564,13 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
[self.appearanceCache removeAllObjects]; [self.appearanceCache removeAllObjects];
} }
#pragma mark - IncognitoReauthObserver
- (void)reauthAgent:(IncognitoReauthSceneAgent*)agent
didUpdateAuthenticationRequirement:(BOOL)isRequired {
[self.consumer setItemsRequireAuthentication:isRequired];
}
#pragma mark - Private #pragma mark - Private
// Calls |-populateItems:selectedItemID:| on the consumer. // Calls |-populateItems:selectedItemID:| on the consumer.
...@@ -565,6 +579,9 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list, ...@@ -565,6 +579,9 @@ web::WebState* GetWebStateWithId(WebStateList* web_state_list,
[self.consumer populateItems:CreateItems(self.webStateList) [self.consumer populateItems:CreateItems(self.webStateList)
selectedItemID:GetActiveTabId(self.webStateList)]; selectedItemID:GetActiveTabId(self.webStateList)];
} }
[self.consumer
setItemsRequireAuthentication:self.reauthAgent.authenticationRequired];
} }
// Removes |self.syncedClosedTabsCount| most recent entries from the // Removes |self.syncedClosedTabsCount| most recent entries from the
......
...@@ -161,6 +161,10 @@ std::unique_ptr<KeyedService> BuildFakeTabRestoreService( ...@@ -161,6 +161,10 @@ std::unique_ptr<KeyedService> BuildFakeTabRestoreService(
@synthesize items = _items; @synthesize items = _items;
@synthesize selectedItemID = _selectedItemID; @synthesize selectedItemID = _selectedItemID;
- (void)setItemsRequireAuthentication:(BOOL)require {
// No-op.
}
- (void)populateItems:(NSArray<TabSwitcherItem*>*)items - (void)populateItems:(NSArray<TabSwitcherItem*>*)items
selectedItemID:(NSString*)selectedItemID { selectedItemID:(NSString*)selectedItemID {
self.selectedItemID = selectedItemID; self.selectedItemID = selectedItemID;
...@@ -264,7 +268,8 @@ class TabGridMediatorTest : public PlatformTest { ...@@ -264,7 +268,8 @@ class TabGridMediatorTest : public PlatformTest {
TabIdTabHelper::FromWebState(web_state_list_->GetWebStateAt(1)) TabIdTabHelper::FromWebState(web_state_list_->GetWebStateAt(1))
->tab_id(); ->tab_id();
consumer_ = [[FakeConsumer alloc] init]; consumer_ = [[FakeConsumer alloc] init];
mediator_ = [[TabGridMediator alloc] initWithConsumer:consumer_]; mediator_ = [[TabGridMediator alloc] initWithConsumer:consumer_
reauthAgent:nil];
mediator_.browser = browser_.get(); mediator_.browser = browser_.get();
mediator_.tabRestoreService = tab_restore_service_; mediator_.tabRestoreService = tab_restore_service_;
} }
......
...@@ -991,6 +991,15 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -991,6 +991,15 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
[self configureDoneButtonBasedOnPage:self.currentPage]; [self configureDoneButtonBasedOnPage:self.currentPage];
} }
[self configureCloseAllButtonForCurrentPageAndUndoAvailability]; [self configureCloseAllButtonForCurrentPageAndUndoAvailability];
[self configureNewTabButtonBasedOnContentPermissions];
}
- (void)configureNewTabButtonBasedOnContentPermissions {
BOOL allowNewTab =
!((self.currentPage == TabGridPageIncognitoTabs) &&
self.incognitoTabsViewController.contentNeedsAuthentication);
[self.bottomToolbar setNewTabButtonEnabled:allowNewTab];
} }
- (void)configureDoneButtonBasedOnPage:(TabGridPage)page { - (void)configureDoneButtonBasedOnPage:(TabGridPage)page {
...@@ -1002,8 +1011,14 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -1002,8 +1011,14 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
} }
// The Done button should have the same behavior as the other buttons on the // The Done button should have the same behavior as the other buttons on the
// top Toolbar. // top Toolbar.
self.doneButton.enabled = !gridViewController.gridEmpty && BOOL incognitoTabsNeedsAuth =
self.topToolbar.pageControl.userInteractionEnabled; (self.currentPage == TabGridPageIncognitoTabs &&
self.incognitoTabsViewController.contentNeedsAuthentication);
self.doneButton.enabled =
!gridViewController.gridEmpty &&
self.topToolbar.pageControl.userInteractionEnabled &&
!incognitoTabsNeedsAuth;
} }
- (void)configureCloseAllButtonForCurrentPageAndUndoAvailability { - (void)configureCloseAllButtonForCurrentPageAndUndoAvailability {
...@@ -1020,8 +1035,14 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -1020,8 +1035,14 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
// Otherwise setup as a Close All button. // Otherwise setup as a Close All button.
GridViewController* gridViewController = GridViewController* gridViewController =
[self gridViewControllerForPage:self.currentPage]; [self gridViewControllerForPage:self.currentPage];
self.closeAllButton.enabled = BOOL enabled =
gridViewController == nil ? NO : !gridViewController.gridEmpty; (gridViewController == nil) ? NO : !gridViewController.gridEmpty;
BOOL incognitoTabsNeedsAuth =
(self.currentPage == TabGridPageIncognitoTabs &&
self.incognitoTabsViewController.contentNeedsAuthentication);
enabled = enabled && !incognitoTabsNeedsAuth;
self.closeAllButton.enabled = enabled;
self.closeAllButton.title = self.closeAllButton.title =
l10n_util::GetNSString(IDS_IOS_TAB_GRID_CLOSE_ALL_BUTTON); l10n_util::GetNSString(IDS_IOS_TAB_GRID_CLOSE_ALL_BUTTON);
// Setting the |accessibilityIdentifier| seems to trigger layout, which causes // Setting the |accessibilityIdentifier| seems to trigger layout, which causes
...@@ -1386,6 +1407,11 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) { ...@@ -1386,6 +1407,11 @@ NSUInteger GetPageIndexFromPage(TabGridPage page) {
: CGAffineTransformIdentity; : CGAffineTransformIdentity;
} }
- (void)gridViewController:(GridViewController*)gridViewController
contentNeedsAuthenticationChanged:(BOOL)needsAuth {
[self configureButtonsForActiveAndCurrentPage];
}
#pragma mark - Control actions #pragma mark - Control actions
- (void)doneButtonTapped:(id)sender { - (void)doneButtonTapped:(id)sender {
......
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