Commit f3d94c20 authored by Rohit Rao's avatar Rohit Rao Committed by Commit Bot

[ios] Enables an experiment for presenting BVC and the stack view.

These view controllers are currently contained children of MainViewController.
When the experiment is enabled, they are presented instead.

This CL also adds a completion handler to [MainViewController
setActiveViewController:completion:].  This handler will be used in a future CL.

BUG=768563

Change-Id: Iefead6de0a1471dcd07c67d634acbab15c70b9c3
Reviewed-on: https://chromium-review.googlesource.com/683196Reviewed-by: default avataredchin <edchin@chromium.org>
Commit-Queue: Rohit Rao (ping after 24h) <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#504451}
parent fa355f00
...@@ -1742,7 +1742,8 @@ const int kExternalFilesCleanupDelaySeconds = 60; ...@@ -1742,7 +1742,8 @@ const int kExternalFilesCleanupDelaySeconds = 60;
} }
- (void)displayCurrentBVC { - (void)displayCurrentBVC {
self.mainViewController.activeViewController = self.currentBVC; [self.mainViewController setActiveViewController:self.currentBVC
completion:nil];
} }
- (TabModel*)currentTabModel { - (TabModel*)currentTabModel {
...@@ -1806,12 +1807,14 @@ const int kExternalFilesCleanupDelaySeconds = 60; ...@@ -1806,12 +1807,14 @@ const int kExternalFilesCleanupDelaySeconds = 60;
mainBVC:self.mainBVC mainBVC:self.mainBVC
otrBVC:self.otrBVC]; otrBVC:self.otrBVC];
[_tabSwitcherController setTransitionContext:transitionContext]; [_tabSwitcherController setTransitionContext:transitionContext];
self.mainViewController.activeViewController = _tabSwitcherController; [self.mainViewController setActiveViewController:_tabSwitcherController
completion:nil];
[_tabSwitcherController showWithSelectedTabAnimation]; [_tabSwitcherController showWithSelectedTabAnimation];
} else { } else {
// User interaction is disabled when the stack controller is dismissed. // User interaction is disabled when the stack controller is dismissed.
[[_tabSwitcherController view] setUserInteractionEnabled:YES]; [[_tabSwitcherController view] setUserInteractionEnabled:YES];
self.mainViewController.activeViewController = _tabSwitcherController; [self.mainViewController setActiveViewController:_tabSwitcherController
completion:nil];
[_tabSwitcherController showWithSelectedTabAnimation]; [_tabSwitcherController showWithSelectedTabAnimation];
} }
} }
......
...@@ -14,6 +14,7 @@ source_set("main") { ...@@ -14,6 +14,7 @@ source_set("main") {
"main_view_controller.mm", "main_view_controller.mm",
] ]
deps = [ deps = [
":feature_flags",
"//base", "//base",
"//ios/chrome/browser", "//ios/chrome/browser",
"//ios/chrome/browser/browser_state", "//ios/chrome/browser/browser_state",
...@@ -54,13 +55,16 @@ source_set("unit_tests") { ...@@ -54,13 +55,16 @@ source_set("unit_tests") {
"main_view_controller_unittest.mm", "main_view_controller_unittest.mm",
] ]
deps = [ deps = [
":feature_flags",
":main", ":main",
"//base", "//base",
"//base/test:test_support",
"//components/bookmarks/test", "//components/bookmarks/test",
"//ios/chrome/browser/bookmarks", "//ios/chrome/browser/bookmarks",
"//ios/chrome/browser/browser_state:test_support", "//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/tabs", "//ios/chrome/browser/tabs",
"//ios/chrome/browser/ui:ui_internal", "//ios/chrome/browser/ui:ui_internal",
"//ios/chrome/test:block_cleanup_test",
"//ios/web/public/test", "//ios/web/public/test",
"//testing/gtest", "//testing/gtest",
] ]
......
...@@ -7,12 +7,21 @@ ...@@ -7,12 +7,21 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "base/ios/block_types.h"
// A UIViewController that provides a property to maintain a single child view // A UIViewController that provides a property to maintain a single child view
// controller. // controller.
@interface MainViewController : UIViewController @interface MainViewController : UIViewController
// The child view controller, if any, that is active. Assigning to
// |activeViewController| will remove any previous active view controller. // The view controller, if any, that is active.
@property(nonatomic, strong) UIViewController* activeViewController; @property(nonatomic, readonly, strong) UIViewController* activeViewController;
// Sets the active view controller, replacing any previously-active view
// controller. The |completion| block is invoked once the new view controller
// is presented or added as a child.
- (void)setActiveViewController:(UIViewController*)activeViewController
completion:(ProceduralBlock)completion;
@end @end
#endif // IOS_CHROME_BROWSER_UI_MAIN_MAIN_VIEW_CONTROLLER_H_ #endif // IOS_CHROME_BROWSER_UI_MAIN_MAIN_VIEW_CONTROLLER_H_
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#import "ios/chrome/browser/ui/main/main_view_controller.h" #import "ios/chrome/browser/ui/main/main_view_controller.h"
#import "base/logging.h" #import "base/logging.h"
#include "ios/chrome/browser/ui/main/main_feature_flags.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -13,10 +14,15 @@ ...@@ -13,10 +14,15 @@
@implementation MainViewController @implementation MainViewController
- (UIViewController*)activeViewController { - (UIViewController*)activeViewController {
return [self.childViewControllers firstObject]; if (TabSwitcherPresentsBVCEnabled()) {
return self.presentedViewController;
} else {
return [self.childViewControllers firstObject];
}
} }
- (void)setActiveViewController:(UIViewController*)activeViewController { - (void)setActiveViewController:(UIViewController*)activeViewController
completion:(void (^)())completion {
DCHECK(activeViewController); DCHECK(activeViewController);
if (self.activeViewController == activeViewController) if (self.activeViewController == activeViewController)
return; return;
...@@ -24,27 +30,54 @@ ...@@ -24,27 +30,54 @@
// TODO(crbug.com/546189): DCHECK here that there isn't a modal view // TODO(crbug.com/546189): DCHECK here that there isn't a modal view
// controller showing once the known violations of that are fixed. // controller showing once the known violations of that are fixed.
// Remove the current active view controller, if any. if (TabSwitcherPresentsBVCEnabled()) {
if (self.activeViewController) { if (self.activeViewController) {
[self.activeViewController willMoveToParentViewController:nil]; // This call must be to super, as the override of
[self.activeViewController.view removeFromSuperview]; // dismissViewControllerAnimated:completion: below tries to dismiss using
[self.activeViewController removeFromParentViewController]; // self.activeViewController, but this call explicitly needs to present
// using the MainViewController itself.
[super dismissViewControllerAnimated:NO completion:nil];
}
// This call must be to super, as the override of
// presentViewController:animated:completion: below tries to present using
// self.activeViewController, but this call explicitly needs to present
// using the MainViewController itself.
[super
presentViewController:activeViewController
animated:NO
completion:^{
DCHECK(self.activeViewController == activeViewController);
if (completion) {
completion();
}
}];
} else {
// Remove the current active view controller, if any.
if (self.activeViewController) {
[self.activeViewController willMoveToParentViewController:nil];
[self.activeViewController.view removeFromSuperview];
[self.activeViewController removeFromParentViewController];
}
DCHECK(self.activeViewController == nil);
DCHECK(self.view.subviews.count == 0);
// Add the new active view controller.
[self addChildViewController:activeViewController];
self.activeViewController.view.frame = self.view.bounds;
[self.view addSubview:self.activeViewController.view];
[activeViewController didMoveToParentViewController:self];
// Let the system know that the child has changed so appearance updates can
// be made.
[self setNeedsStatusBarAppearanceUpdate];
DCHECK(self.activeViewController == activeViewController);
if (completion) {
completion();
}
} }
DCHECK(self.activeViewController == nil);
DCHECK(self.view.subviews.count == 0);
// Add the new active view controller.
[self addChildViewController:activeViewController];
self.activeViewController.view.frame = self.view.bounds;
[self.view addSubview:self.activeViewController.view];
[activeViewController didMoveToParentViewController:self];
// Let the system know that the child has changed so appearance updates can
// be made.
[self setNeedsStatusBarAppearanceUpdate];
DCHECK(self.activeViewController == activeViewController);
} }
#pragma mark - UIViewController methods #pragma mark - UIViewController methods
......
...@@ -6,6 +6,10 @@ ...@@ -6,6 +6,10 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "base/test/ios/wait_util.h"
#include "base/test/scoped_feature_list.h"
#include "ios/chrome/browser/ui/main/main_feature_flags.h"
#import "ios/chrome/test/block_cleanup_test.h"
#include "testing/gtest_mac.h" #include "testing/gtest_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -52,7 +56,40 @@ ...@@ -52,7 +56,40 @@
namespace { namespace {
TEST(MainViewControllerTest, ActiveVC) { class MainViewControllerTest : public BlockCleanupTest {
public:
MainViewControllerTest() = default;
~MainViewControllerTest() override = default;
void TearDown() override {
if (original_root_view_controller_) {
[[UIApplication sharedApplication] keyWindow].rootViewController =
original_root_view_controller_;
original_root_view_controller_ = nil;
}
}
// Sets the current key window's rootViewController and saves a pointer to
// the original VC to allow restoring it at the end of the test.
void SetRootViewController(UIViewController* new_root_view_controller) {
original_root_view_controller_ =
[[[UIApplication sharedApplication] keyWindow] rootViewController];
[[UIApplication sharedApplication] keyWindow].rootViewController =
new_root_view_controller;
}
private:
// The key window's original root view controller, which must be restored at
// the end of the test.
UIViewController* original_root_view_controller_;
DISALLOW_COPY_AND_ASSIGN(MainViewControllerTest);
};
TEST_F(MainViewControllerTest, ActiveVC) {
base::test::ScopedFeatureList disable_feature;
disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
MainViewController* main_view_controller = [[MainViewController alloc] init]; MainViewController* main_view_controller = [[MainViewController alloc] init];
UIViewController* child_view_controller_1 = [[UIViewController alloc] init]; UIViewController* child_view_controller_1 = [[UIViewController alloc] init];
UIViewController* child_view_controller_2 = [[UIViewController alloc] init]; UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
...@@ -71,7 +108,10 @@ TEST(MainViewControllerTest, ActiveVC) { ...@@ -71,7 +108,10 @@ TEST(MainViewControllerTest, ActiveVC) {
[main_view_controller activeViewController]); [main_view_controller activeViewController]);
} }
TEST(MainViewControllerTest, SetActiveVC) { TEST_F(MainViewControllerTest, SetActiveVCWithContainment) {
base::test::ScopedFeatureList disable_feature;
disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
MainViewController* main_view_controller = [[MainViewController alloc] init]; MainViewController* main_view_controller = [[MainViewController alloc] init];
CGRect windowRect = CGRectMake(0, 0, 200, 200); CGRect windowRect = CGRectMake(0, 0, 200, 200);
[main_view_controller view].frame = windowRect; [main_view_controller view].frame = windowRect;
...@@ -80,7 +120,8 @@ TEST(MainViewControllerTest, SetActiveVC) { ...@@ -80,7 +120,8 @@ TEST(MainViewControllerTest, SetActiveVC) {
[[MockViewController alloc] init]; [[MockViewController alloc] init];
[child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10); [child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10);
[main_view_controller setActiveViewController:child_view_controller_1]; [main_view_controller setActiveViewController:child_view_controller_1
completion:nil];
EXPECT_EQ(child_view_controller_1, EXPECT_EQ(child_view_controller_1,
[[main_view_controller childViewControllers] firstObject]); [[main_view_controller childViewControllers] firstObject]);
EXPECT_EQ([child_view_controller_1 view], EXPECT_EQ([child_view_controller_1 view],
...@@ -97,7 +138,8 @@ TEST(MainViewControllerTest, SetActiveVC) { ...@@ -97,7 +138,8 @@ TEST(MainViewControllerTest, SetActiveVC) {
[child_view_controller_1 willMoveToParentViewControllerArgument]); [child_view_controller_1 willMoveToParentViewControllerArgument]);
UIViewController* child_view_controller_2 = [[UIViewController alloc] init]; UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
[main_view_controller setActiveViewController:child_view_controller_2]; [main_view_controller setActiveViewController:child_view_controller_2
completion:nil];
EXPECT_EQ(child_view_controller_2, EXPECT_EQ(child_view_controller_2,
[[main_view_controller childViewControllers] firstObject]); [[main_view_controller childViewControllers] firstObject]);
EXPECT_EQ(1U, [[main_view_controller childViewControllers] count]); EXPECT_EQ(1U, [[main_view_controller childViewControllers] count]);
...@@ -112,18 +154,18 @@ TEST(MainViewControllerTest, SetActiveVC) { ...@@ -112,18 +154,18 @@ TEST(MainViewControllerTest, SetActiveVC) {
[child_view_controller_1 didMoveToParentViewControllerArgument]); [child_view_controller_1 didMoveToParentViewControllerArgument]);
} }
TEST(MainViewControllerTest, StatusBar) { TEST_F(MainViewControllerTest, StatusBar) {
base::test::ScopedFeatureList disable_feature;
disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
MainViewController* main_view_controller = [[MainViewController alloc] init]; MainViewController* main_view_controller = [[MainViewController alloc] init];
// MVC needs to be the root view controller for this to work, so save the SetRootViewController(main_view_controller);
// current one and restore it at the end of the test.
UIViewController* root_view_controller =
[[[UIApplication sharedApplication] keyWindow] rootViewController];
[[UIApplication sharedApplication] keyWindow].rootViewController =
main_view_controller;
UIViewController* status_bar_visible_view_controller = UIViewController* status_bar_visible_view_controller =
[[UIViewController alloc] init]; [[UIViewController alloc] init];
[main_view_controller [main_view_controller
setActiveViewController:status_bar_visible_view_controller]; setActiveViewController:status_bar_visible_view_controller
completion:nil];
// Check if the status bar is hidden by testing if the status bar rect is // Check if the status bar is hidden by testing if the status bar rect is
// CGRectZero. // CGRectZero.
EXPECT_FALSE(CGRectEqualToRect( EXPECT_FALSE(CGRectEqualToRect(
...@@ -131,13 +173,101 @@ TEST(MainViewControllerTest, StatusBar) { ...@@ -131,13 +173,101 @@ TEST(MainViewControllerTest, StatusBar) {
HiddenStatusBarViewController* status_bar_hidden_view_controller = HiddenStatusBarViewController* status_bar_hidden_view_controller =
[[HiddenStatusBarViewController alloc] init]; [[HiddenStatusBarViewController alloc] init];
[main_view_controller [main_view_controller
setActiveViewController:status_bar_hidden_view_controller]; setActiveViewController:status_bar_hidden_view_controller
completion:nil];
EXPECT_EQ([main_view_controller childViewControllerForStatusBarHidden], EXPECT_EQ([main_view_controller childViewControllerForStatusBarHidden],
status_bar_hidden_view_controller); status_bar_hidden_view_controller);
EXPECT_TRUE(CGRectEqualToRect( EXPECT_TRUE(CGRectEqualToRect(
[UIApplication sharedApplication].statusBarFrame, CGRectZero)); [UIApplication sharedApplication].statusBarFrame, CGRectZero));
[[UIApplication sharedApplication] keyWindow].rootViewController = }
root_view_controller;
// Tests that completion handlers are called properly after the new view
// controller is made active, when using containment
TEST_F(MainViewControllerTest, CompletionHandlersWithContainment) {
base::test::ScopedFeatureList disable_feature;
disable_feature.InitAndDisableFeature(kTabSwitcherPresentsBVC);
MainViewController* main_view_controller = [[MainViewController alloc] init];
CGRect windowRect = CGRectMake(0, 0, 200, 200);
[main_view_controller view].frame = windowRect;
UIViewController* child_view_controller_1 = [[UIViewController alloc] init];
[child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10);
UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
[child_view_controller_2 view].frame = CGRectMake(20, 20, 10, 10);
// Tests that the completion handler is called when there is no preexisting
// active view controller.
__block BOOL completion_handler_was_called = false;
[main_view_controller setActiveViewController:child_view_controller_1
completion:^{
completion_handler_was_called = YES;
}];
base::test::ios::WaitUntilCondition(^bool() {
return completion_handler_was_called;
});
ASSERT_TRUE(completion_handler_was_called);
// Tests that the completion handler is called when replacing an existing
// active view controller.
completion_handler_was_called = NO;
[main_view_controller setActiveViewController:child_view_controller_2
completion:^{
completion_handler_was_called = YES;
}];
base::test::ios::WaitUntilCondition(^bool() {
return completion_handler_was_called;
});
ASSERT_TRUE(completion_handler_was_called);
}
// Tests that setting the active view controller work and that completion
// handlers are called properly after the new view controller is made active,
// when using presentation.
TEST_F(MainViewControllerTest,
SetActiveVCAndCompletionHandlersWithPresentation) {
base::test::ScopedFeatureList enable_feature;
enable_feature.InitAndEnableFeature(kTabSwitcherPresentsBVC);
MainViewController* main_view_controller = [[MainViewController alloc] init];
SetRootViewController(main_view_controller);
CGRect windowRect = CGRectMake(0, 0, 200, 200);
[main_view_controller view].frame = windowRect;
UIViewController* child_view_controller_1 = [[UIViewController alloc] init];
[child_view_controller_1 view].frame = CGRectMake(0, 0, 10, 10);
UIViewController* child_view_controller_2 = [[UIViewController alloc] init];
[child_view_controller_2 view].frame = CGRectMake(20, 20, 10, 10);
// Tests that the completion handler is called when there is no preexisting
// active view controller.
__block BOOL completion_handler_was_called = false;
[main_view_controller setActiveViewController:child_view_controller_1
completion:^{
completion_handler_was_called = YES;
}];
base::test::ios::WaitUntilCondition(^bool() {
return completion_handler_was_called;
});
ASSERT_TRUE(completion_handler_was_called);
EXPECT_EQ(main_view_controller.presentedViewController,
child_view_controller_1);
// Tests that the completion handler is called when replacing an existing
// active view controller.
completion_handler_was_called = NO;
[main_view_controller setActiveViewController:child_view_controller_2
completion:^{
completion_handler_was_called = YES;
}];
base::test::ios::WaitUntilCondition(^bool() {
return completion_handler_was_called;
});
ASSERT_TRUE(completion_handler_was_called);
EXPECT_EQ(main_view_controller.presentedViewController,
child_view_controller_2);
} }
} // namespace } // namespace
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