Commit 2278b7f5 authored by Eugene But's avatar Eugene But Committed by Commit Bot

Create SadTabCoordinator which owns SadTabViewController.

SadTabCoordinator will be used instead SadTabLegacyCoordinator
for SadTab UI presentation. SadTabLegacyCoordinator does not
use view controller and presents SadTab UI with
WebState::ShowTransientView, which is misuse of WebState API.

Bug: 901563

Change-Id: I7c29c82d0036c05d06e44e8806ff0b82adad34ca
Reviewed-on: https://chromium-review.googlesource.com/c/1320593Reviewed-by: default avatarKurt Horimoto <kkhorimoto@chromium.org>
Commit-Queue: Eugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606321}
parent 887b9cec
...@@ -33,14 +33,20 @@ source_set("sad_tab") { ...@@ -33,14 +33,20 @@ source_set("sad_tab") {
source_set("coordinator") { source_set("coordinator") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
"sad_tab_coordinator.h",
"sad_tab_coordinator.mm",
"sad_tab_legacy_coordinator.h", "sad_tab_legacy_coordinator.h",
"sad_tab_legacy_coordinator.mm", "sad_tab_legacy_coordinator.mm",
] ]
deps = [ deps = [
":sad_tab", ":sad_tab",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/ui/commands", "//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/util",
"//ios/chrome/browser/web", "//ios/chrome/browser/web",
"//ios/chrome/browser/web:tab_helper_delegates", "//ios/chrome/browser/web:tab_helper_delegates",
"//ios/chrome/common/ui_util",
"//ios/web", "//ios/web",
] ]
} }
...@@ -49,11 +55,19 @@ source_set("unit_tests") { ...@@ -49,11 +55,19 @@ source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
testonly = true testonly = true
sources = [ sources = [
"sad_tab_coordinator_unittest.mm",
"sad_tab_view_controller_unittest.mm", "sad_tab_view_controller_unittest.mm",
] ]
deps = [ deps = [
"//components/strings:components_strings_grit", "//components/strings:components_strings_grit",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/sad_tab", "//ios/chrome/browser/ui/sad_tab",
"//ios/chrome/browser/ui/sad_tab:coordinator",
"//ios/chrome/browser/ui/util",
"//ios/chrome/common/ui_util",
"//ios/web/public/test",
"//ios/web/public/test/fakes",
"//testing/gtest", "//testing/gtest",
"//third_party/ocmock", "//third_party/ocmock",
"//ui/base", "//ui/base",
......
// Copyright 2018 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_SAD_TAB_SAD_TAB_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_SAD_TAB_SAD_TAB_COORDINATOR_H_
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
#import "ios/chrome/browser/web/sad_tab_tab_helper_delegate.h"
@protocol ApplicationCommands;
@protocol BrowserCommands;
@class SadTabCoordinator;
@protocol SadTabCoordinatorDelegate
// Called from -[SadTabCoordinator start].
- (void)sadTabCoordinatorDidStart:(SadTabCoordinator*)sadTabCoordinator;
@end
// Coordinator that displays a SadTab view.
@interface SadTabCoordinator : ChromeCoordinator<SadTabTabHelperDelegate>
@property(nonatomic, weak) id<ApplicationCommands, BrowserCommands> dispatcher;
@property(nonatomic, weak) id<SadTabCoordinatorDelegate> delegate;
@property(nonatomic, readonly) UIViewController* viewController;
// YES if page load for this URL has failed more than once.
@property(nonatomic) BOOL repeatedFailure;
@end
#endif // IOS_CHROME_BROWSER_UI_SAD_TAB_SAD_TAB_COORDINATOR_H_
// Copyright 2018 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/sad_tab/sad_tab_coordinator.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/browser_commands.h"
#import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
#import "ios/chrome/browser/ui/sad_tab/sad_tab_view_controller.h"
#import "ios/chrome/browser/web/sad_tab_tab_helper.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#import "ios/web/public/web_state/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface SadTabCoordinator ()<SadTabViewControllerDelegate> {
SadTabViewController* _viewController;
}
@end
@implementation SadTabCoordinator
@synthesize dispatcher = _dispatcher;
@synthesize delegate = _delegate;
@synthesize viewController = _viewController;
@synthesize repeatedFailure = _repeatedFailure;
- (void)start {
if (_viewController)
return;
_viewController = [[SadTabViewController alloc] init];
_viewController.delegate = self;
_viewController.offTheRecord = self.browserState->IsOffTheRecord();
_viewController.repeatedFailure = _repeatedFailure;
[self.baseViewController addChildViewController:_viewController];
[self.baseViewController.view addSubview:_viewController.view];
[_viewController didMoveToParentViewController:self.baseViewController];
[self.delegate sadTabCoordinatorDidStart:self];
}
- (void)stop {
if (!_viewController)
return;
[_viewController willMoveToParentViewController:nil];
[_viewController.view removeFromSuperview];
[_viewController removeFromParentViewController];
_viewController = nil;
}
#pragma mark - SadTabViewDelegate
- (void)sadTabViewControllerShowReportAnIssue:
(SadTabViewController*)sadTabViewController {
[self.dispatcher showReportAnIssueFromViewController:self.baseViewController];
}
- (void)sadTabViewController:(SadTabViewController*)sadTabViewController
showSuggestionsPageWithURL:(const GURL&)URL {
OpenNewTabCommand* command = [OpenNewTabCommand commandWithURLFromChrome:URL];
[self.dispatcher openURLInNewTab:command];
}
- (void)sadTabViewControllerReload:(SadTabViewController*)sadTabViewController {
[self.dispatcher reload];
}
#pragma mark - SadTabTabHelperDelegate
- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
presentSadTabForWebState:(web::WebState*)webState
repeatedFailure:(BOOL)repeatedFailure {
if (!webState->IsVisible())
return;
_repeatedFailure = repeatedFailure;
[self start];
}
- (void)sadTabTabHelperDismissSadTab:(SadTabTabHelper*)tabHelper {
[self stop];
}
- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
didShowForRepeatedFailure:(BOOL)repeatedFailure {
_repeatedFailure = repeatedFailure;
[self start];
}
- (void)sadTabTabHelperDidHide:(SadTabTabHelper*)tabHelper {
[self stop];
}
@end
// Copyright 2018 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/sad_tab/sad_tab_coordinator.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/browser_commands.h"
#import "ios/chrome/browser/ui/sad_tab/sad_tab_view_controller.h"
#import "ios/chrome/browser/ui/util/named_guide.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/test/test_web_thread_bundle.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"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Test fixture for testing SadTabCoordinator class.
class SadTabCoordinatorTest : public PlatformTest {
protected:
SadTabCoordinatorTest()
: base_view_controller_([[UIViewController alloc] init]),
browser_state_(TestChromeBrowserState::Builder().Build()) {
UILayoutGuide* guide = [[NamedGuide alloc] initWithName:kContentAreaGuide];
[base_view_controller_.view addLayoutGuide:guide];
AddSameConstraints(guide, base_view_controller_.view);
}
web::TestWebThreadBundle thread_bundle_;
UIViewController* base_view_controller_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
};
// Tests starting coordinator.
TEST_F(SadTabCoordinatorTest, Start) {
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
[coordinator start];
// Verify that presented view controller is SadTabViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
SadTabViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([SadTabViewController class], [view_controller class]);
// Verify SadTabViewController state.
EXPECT_FALSE(view_controller.offTheRecord);
EXPECT_FALSE(view_controller.repeatedFailure);
}
// Tests |sadTabCoordinatorDidStart:| delegate call.
TEST_F(SadTabCoordinatorTest, Delegate) {
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
id delegate = OCMStrictProtocolMock(@protocol(SadTabCoordinatorDelegate));
coordinator.delegate = delegate;
OCMExpect([delegate sadTabCoordinatorDidStart:coordinator]);
[coordinator start];
EXPECT_OCMOCK_VERIFY(delegate);
}
// Tests stopping coordinator.
TEST_F(SadTabCoordinatorTest, Stop) {
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
[coordinator start];
ASSERT_EQ(1U, base_view_controller_.childViewControllers.count);
[coordinator stop];
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
}
// Tests dismissing Sad Tab.
TEST_F(SadTabCoordinatorTest, Dismiss) {
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
[coordinator start];
ASSERT_EQ(1U, base_view_controller_.childViewControllers.count);
[coordinator sadTabTabHelperDismissSadTab:nullptr];
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
}
// Tests hiding Sad Tab.
TEST_F(SadTabCoordinatorTest, Hide) {
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
[coordinator start];
ASSERT_EQ(1U, base_view_controller_.childViewControllers.count);
[coordinator sadTabTabHelperDidHide:nullptr];
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
}
// Tests SadTabViewController state for the first failure in non-incognito mode.
TEST_F(SadTabCoordinatorTest, FirstFailureInNonIncognito) {
web::TestWebState web_state;
web_state.WasShown();
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
[coordinator sadTabTabHelper:nullptr
presentSadTabForWebState:&web_state
repeatedFailure:NO];
// Verify that presented view controller is SadTabViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
SadTabViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([SadTabViewController class], [view_controller class]);
// Verify SadTabViewController state.
EXPECT_FALSE(view_controller.offTheRecord);
EXPECT_FALSE(view_controller.repeatedFailure);
}
// Tests SadTabViewController state for the repeated failure in incognito mode.
TEST_F(SadTabCoordinatorTest, FirstFailureInIncognito) {
web::TestWebState web_state;
web_state.WasShown();
ios::ChromeBrowserState* otr_browser_state =
browser_state_->GetOffTheRecordChromeBrowserState();
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:otr_browser_state];
[coordinator sadTabTabHelper:nullptr
presentSadTabForWebState:&web_state
repeatedFailure:YES];
// Verify that presented view controller is SadTabViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
SadTabViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([SadTabViewController class], [view_controller class]);
// Verify SadTabViewController state.
EXPECT_TRUE(view_controller.offTheRecord);
EXPECT_TRUE(view_controller.repeatedFailure);
}
// Tests SadTabViewController state for the repeated failure in incognito mode.
TEST_F(SadTabCoordinatorTest, ShowFirstFailureInIncognito) {
ios::ChromeBrowserState* otr_browser_state =
browser_state_->GetOffTheRecordChromeBrowserState();
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:otr_browser_state];
[coordinator sadTabTabHelper:nullptr didShowForRepeatedFailure:YES];
// Verify that presented view controller is SadTabViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
SadTabViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([SadTabViewController class], [view_controller class]);
// Verify SadTabViewController state.
EXPECT_TRUE(view_controller.offTheRecord);
EXPECT_TRUE(view_controller.repeatedFailure);
}
// Tests action button tap for the first failure.
TEST_F(SadTabCoordinatorTest, FirstFailureAction) {
web::TestWebState web_state;
web_state.WasShown();
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
coordinator.dispatcher = OCMStrictProtocolMock(@protocol(BrowserCommands));
OCMExpect([coordinator.dispatcher reload]);
[coordinator sadTabTabHelper:nullptr
presentSadTabForWebState:&web_state
repeatedFailure:NO];
// Verify that presented view controller is SadTabViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
SadTabViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([SadTabViewController class], [view_controller class]);
// Verify dispatcher's message.
[view_controller.actionButton
sendActionsForControlEvents:UIControlEventTouchUpInside];
EXPECT_OCMOCK_VERIFY(coordinator.dispatcher);
}
// Tests action button tap for the repeated failure.
TEST_F(SadTabCoordinatorTest, RepeatedFailureAction) {
web::TestWebState web_state;
web_state.WasShown();
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
coordinator.dispatcher =
OCMStrictProtocolMock(@protocol(ApplicationCommands));
OCMExpect([coordinator.dispatcher
showReportAnIssueFromViewController:base_view_controller_]);
[coordinator sadTabTabHelper:nullptr
presentSadTabForWebState:&web_state
repeatedFailure:YES];
// Verify that presented view controller is SadTabViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
SadTabViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([SadTabViewController class], [view_controller class]);
// Verify dispatcher's message.
[view_controller.actionButton
sendActionsForControlEvents:UIControlEventTouchUpInside];
EXPECT_OCMOCK_VERIFY(coordinator.dispatcher);
}
// Tests that view controller is not presented for the hidden web state.
TEST_F(SadTabCoordinatorTest, IgnoreSadTabFromHiddenWebState) {
web::TestWebState web_state;
SadTabCoordinator* coordinator = [[SadTabCoordinator alloc]
initWithBaseViewController:base_view_controller_
browserState:browser_state_.get()];
[coordinator sadTabTabHelper:nullptr
presentSadTabForWebState:&web_state
repeatedFailure:NO];
// Verify that view controller was not presented for the hidden web state.
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
}
...@@ -53,4 +53,17 @@ ...@@ -53,4 +53,17 @@
webState->ShowTransientContentView(contentView); webState->ShowTransientContentView(contentView);
} }
- (void)sadTabTabHelperDismissSadTab:(SadTabTabHelper*)tabHelper {
// Transient Content View is dismissed automatically.
}
- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
didShowForRepeatedFailure:(BOOL)repeatedFailure {
// No-op. Transient content view was not removed when Tab was hidden.
}
- (void)sadTabTabHelperDidHide:(SadTabTabHelper*)tabHelper {
// No-op. Transient content view should not be removed when Tab is hidden.
}
@end @end
...@@ -16,11 +16,21 @@ class WebState; ...@@ -16,11 +16,21 @@ class WebState;
// Delegate for SadTabTabHelper. // Delegate for SadTabTabHelper.
@protocol SadTabTabHelperDelegate<NSObject> @protocol SadTabTabHelperDelegate<NSObject>
// Asks the delegate to present a SadTabView. // Asks the delegate to present Sad Tab UI.
- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper - (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
presentSadTabForWebState:(web::WebState*)webState presentSadTabForWebState:(web::WebState*)webState
repeatedFailure:(BOOL)repeatedFailure; repeatedFailure:(BOOL)repeatedFailure;
// Asks the delegate to dismiss Sad Tab UI.
- (void)sadTabTabHelperDismissSadTab:(SadTabTabHelper*)tabHelper;
// Called when WebState with Sad Tab was shown.
- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
didShowForRepeatedFailure:(BOOL)repeatedFailure;
// Called when WebState with Sad Tab was hidden.
- (void)sadTabTabHelperDidHide:(SadTabTabHelper*)tabHelper;
@end @end
#endif // IOS_CHROME_BROWSER_WEB_SAD_TAB_TAB_HELPER_DELEGATE_H_ #endif // IOS_CHROME_BROWSER_WEB_SAD_TAB_TAB_HELPER_DELEGATE_H_
...@@ -40,6 +40,16 @@ ...@@ -40,6 +40,16 @@
webState->ShowTransientContentView(contentView); webState->ShowTransientContentView(contentView);
} }
- (void)sadTabTabHelperDismissSadTab:(SadTabTabHelper*)tabHelper {
}
- (void)sadTabTabHelper:(SadTabTabHelper*)tabHelper
didShowForRepeatedFailure:(BOOL)repeatedFailure {
}
- (void)sadTabTabHelperDidHide:(SadTabTabHelper*)tabHelper {
}
@end @end
class SadTabTabHelperTest : public PlatformTest { class SadTabTabHelperTest : public PlatformTest {
......
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