Commit 77189399 authored by Justin Cohen's avatar Justin Cohen Committed by Commit Bot

[ios] Add NewTabPageTabHelper and NewTabPageCoordinator.

In preparation to make the NTP a contained view controller of the BVC, add
helper classes to track activating and deactivating the NTP from a tab helper.

Currently the NTP is displayed as native content within the web content area via
CRWNativeContent.  Rather than go thru ios/web for a native page, instead
create and use a new NTP tab helper and display the NTP directly from within
ios/chrome.

Historically the NTP has been a collection of fake view controllers predating
view controller containment.  While much of the NTP hierarchy has been updated,
this will be the final change required to have a normal NTP presentation with
normal UIViewControllers.

Bug: 826369
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I8517c0231dbfdf42185a7d82ce4ca009e9002bd9
Reviewed-on: https://chromium-review.googlesource.com/c/1280046
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: default avatarKurt Horimoto <kkhorimoto@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599690}
parent 2f5bef20
# 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.
source_set("ntp") {
sources = [
"new_tab_page_tab_helper.h",
"new_tab_page_tab_helper.mm",
"new_tab_page_tab_helper_delegate.h",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
"//base:base",
"//components/strings:components_strings_grit",
"//ios/chrome/browser",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/ntp",
"//ios/chrome/browser/ui/ntp:coordinator",
"//ios/chrome/browser/web_state_list",
"//ios/web/public",
"//ui/base:base",
]
}
source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [
"new_tab_page_tab_helper_unittest.mm",
]
deps = [
"//base/test:test_support",
"//ios/chrome/browser",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/favicon:favicon",
"//ios/chrome/browser/ntp",
"//ios/chrome/browser/ntp_snippets:ntp_snippets",
"//ios/chrome/browser/search_engines:search_engines",
"//ios/chrome/browser/ui",
"//ios/chrome/browser/ui:feature_flags",
"//ios/chrome/browser/ui/ntp",
"//ios/chrome/browser/web_state_list",
"//ios/chrome/browser/web_state_list:test_support",
"//ios/chrome/test:test_support",
"//ios/web/public/test:test",
"//ios/web/public/test/fakes",
"//testing/gtest:gtest",
"//third_party/ocmock",
]
}
// 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_NTP_NEW_TAB_PAGE_TAB_HELPER_H_
#define IOS_CHROME_BROWSER_NTP_NEW_TAB_PAGE_TAB_HELPER_H_
#import <UIKit/UIKit.h>
#include "base/macros.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_owning.h"
#include "ios/web/public/web_state/web_state_observer.h"
#import "ios/web/public/web_state/web_state_user_data.h"
class WebStateList;
@protocol NewTabPageTabHelperDelegate;
@protocol NewTabPageControllerDelegate;
@protocol ApplicationCommands;
@protocol BrowserCommands;
@protocol OmniboxFocuser;
@protocol FakeboxFocuser;
@protocol SnackbarCommands;
@protocol UrlLoader;
@class NewTabPageCoordinator;
// NewTabPageTabHelper which manages a single NTP per tab.
class NewTabPageTabHelper : public web::WebStateObserver,
public web::WebStateUserData<NewTabPageTabHelper> {
public:
~NewTabPageTabHelper() override;
static void CreateForWebState(
web::WebState* web_state,
WebStateList* web_state_list,
id<NewTabPageTabHelperDelegate> delegate,
id<UrlLoader> loader,
id<NewTabPageControllerDelegate> toolbar_delegate,
id<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader> dispatcher);
// Returns true when the current web_state is an NTP and the underlying
// controllers have been created.
bool IsActive() const;
// Returns the current NTP controller.
id<NewTabPageOwning> GetController() const;
// Returns the UIViewController for the current NTP.
// TODO(crbug.com/826369): Currently there's a 1:1 relationship between the
// webState and the NTP, so we can't delegate this coordinator to the BVC.
// Consider sharing one NTP per BVC, and removing this ownership.
UIViewController* GetViewController() const;
// Instructs the current NTP to dismiss any context menus.
void DismissModals() const;
private:
NewTabPageTabHelper(web::WebState* web_state,
WebStateList* web_state_list,
id<NewTabPageTabHelperDelegate> delegate,
id<UrlLoader> loader,
id<NewTabPageControllerDelegate> toolbar_delegate,
id<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader> dispatcher);
// web::WebStateObserver overrides:
void WebStateDestroyed(web::WebState* web_state) override;
void DidStartNavigation(web::WebState* web_state,
web::NavigationContext* navigation_context) override;
// The Objective-C NTP coordinator instance.
// TODO(crbug.com/826369): Currently there's a 1:1 relationship between the
// webState and the NTP, so we can't delegate this coordinator to the BVC.
// Consider sharing one NTP per BVC, and moving this to a delegate.
__strong NewTabPageCoordinator* ntp_coordinator_ = nil;
// Used to present and dismiss the NTP.
__weak id<NewTabPageTabHelperDelegate> delegate_ = nil;
DISALLOW_COPY_AND_ASSIGN(NewTabPageTabHelper);
};
#endif // IOS_CHROME_BROWSER_NTP_NEW_TAB_PAGE_TAB_HELPER_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/ntp/new_tab_page_tab_helper.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/strings/sys_string_conversions.h"
#include "components/strings/grit/components_strings.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/ntp/new_tab_page_tab_helper_delegate.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_coordinator.h"
#include "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/web/public/navigation_item.h"
#import "ios/web/public/navigation_manager.h"
#import "ios/web/public/web_state/navigation_context.h"
#import "ios/web/public/web_state/web_state.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// static
void NewTabPageTabHelper::CreateForWebState(
web::WebState* web_state,
WebStateList* web_state_list,
id<NewTabPageTabHelperDelegate> delegate,
id<UrlLoader> url_loader,
id<NewTabPageControllerDelegate> toolbar_delegate,
id<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader> dispatcher) {
DCHECK(web_state);
if (!FromWebState(web_state)) {
web_state->SetUserData(UserDataKey(),
base::WrapUnique(new NewTabPageTabHelper(
web_state, web_state_list, delegate, url_loader,
toolbar_delegate, dispatcher)));
}
}
NewTabPageTabHelper::~NewTabPageTabHelper() = default;
NewTabPageTabHelper::NewTabPageTabHelper(
web::WebState* web_state,
WebStateList* web_state_list,
id<NewTabPageTabHelperDelegate> delegate,
id<UrlLoader> url_loader,
id<NewTabPageControllerDelegate> toolbar_delegate,
id<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader> dispatcher)
: delegate_(delegate) {
DCHECK(delegate);
DCHECK(base::FeatureList::IsEnabled(kBrowserContainerContainsNTP));
ios::ChromeBrowserState* browser_state =
ios::ChromeBrowserState::FromBrowserState(web_state->GetBrowserState());
ntp_coordinator_ =
[[NewTabPageCoordinator alloc] initWithBrowserState:browser_state];
ntp_coordinator_.webStateList = web_state_list;
ntp_coordinator_.dispatcher = dispatcher;
ntp_coordinator_.URLLoader = url_loader;
ntp_coordinator_.toolbarDelegate = toolbar_delegate;
web_state->AddObserver(this);
if (web_state->GetVisibleURL().GetOrigin() == kChromeUINewTabURL) {
[ntp_coordinator_ start];
}
}
bool NewTabPageTabHelper::IsActive() const {
return ntp_coordinator_.started;
}
UIViewController* NewTabPageTabHelper::GetViewController() const {
DCHECK(IsActive());
return ntp_coordinator_.viewController;
}
id<NewTabPageOwning> NewTabPageTabHelper::GetController() const {
return ntp_coordinator_;
}
void NewTabPageTabHelper::DismissModals() const {
return [ntp_coordinator_ dismissModals];
}
#pragma mark - WebStateObserver
void NewTabPageTabHelper::WebStateDestroyed(web::WebState* web_state) {
web_state->RemoveObserver(this);
}
void NewTabPageTabHelper::DidStartNavigation(
web::WebState* web_state,
web::NavigationContext* navigation_context) {
if (navigation_context->IsSameDocument()) {
return;
}
// Save the NTP scroll offset before we navigate away.
web::NavigationManager* manager = web_state->GetNavigationManager();
if (web_state->GetLastCommittedURL().GetOrigin() == kChromeUINewTabURL) {
DCHECK(IsActive());
web::NavigationItem* item = manager->GetLastCommittedItem();
web::PageDisplayState displayState;
CGPoint scrollOffset = ntp_coordinator_.scrollOffset;
displayState.scroll_state().set_offset_x(scrollOffset.x);
displayState.scroll_state().set_offset_y(scrollOffset.y);
item->SetPageDisplayState(displayState);
}
bool was_active = IsActive();
// Start or stop the NTP.
web::NavigationItem* item = manager->GetPendingItem();
if (item && item->GetURL().GetOrigin() == kChromeUINewTabURL) {
item->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
[ntp_coordinator_ start];
} else {
[ntp_coordinator_ stop];
}
// Tell |delegate_| to show or hide the NTP, if necessary.
if (IsActive() != was_active) {
[delegate_ newTabPageHelperDidChangeVisibility:this];
}
}
// 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_NTP_NEW_TAB_PAGE_TAB_HELPER_DELEGATE_H_
#define IOS_CHROME_BROWSER_NTP_NEW_TAB_PAGE_TAB_HELPER_DELEGATE_H_
class NewTabPageTabHelper;
// Protocol defining a delegate for the NTP tab helper.
@protocol NewTabPageTabHelperDelegate
// Tells the delegate the NTP needs to be shown or hidden.
- (void)newTabPageHelperDidChangeVisibility:(NewTabPageTabHelper*)NTPHelper;
@end
#endif // IOS_CHROME_BROWSER_NTP_NEW_TAB_PAGE_TAB_HELPER_DELEGATE_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/ntp/new_tab_page_tab_helper.h"
#include <memory>
#include "base/feature_list.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
#include "ios/chrome/browser/ntp/new_tab_page_tab_helper_delegate.h"
#include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h"
#include "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/browser/ui/url_loader.h"
#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
#include "ios/chrome/browser/web_state_list/web_state_list.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#import "ios/web/public/test/fakes/fake_navigation_context.h"
#import "ios/web/public/test/fakes/test_navigation_manager.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.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 {
const char kTestURL[] = "http://foo.bar";
} // namespace
@protocol NewTabPageTabDispatcher<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader>
@end
// Test fixture for testing NewTabPageTabHelper class.
class NewTabPageTabHelperTest : public PlatformTest {
protected:
NewTabPageTabHelperTest()
: scoped_browser_state_manager_(
std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.AddTestingFactory(
ios::TemplateURLServiceFactory::GetInstance(),
ios::TemplateURLServiceFactory::GetDefaultFactory());
test_cbs_builder.AddTestingFactory(
IOSChromeContentSuggestionsServiceFactory::GetInstance(),
IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory());
test_cbs_builder.AddTestingFactory(
IOSChromeLargeIconServiceFactory::GetInstance(),
IOSChromeLargeIconServiceFactory::GetDefaultFactory());
chrome_browser_state_ = test_cbs_builder.Build();
auto test_navigation_manager =
std::make_unique<web::TestNavigationManager>();
test_navigation_manager_ = test_navigation_manager.get();
pending_item_ = web::NavigationItem::Create();
test_navigation_manager->SetPendingItem(pending_item_.get());
test_web_state_.SetNavigationManager(std::move(test_navigation_manager));
test_web_state_.SetBrowserState(chrome_browser_state_.get());
delegate_ = OCMProtocolMock(@protocol(NewTabPageTabHelperDelegate));
loader_ = OCMProtocolMock(@protocol(UrlLoader));
toolbar_delegate_ =
OCMProtocolMock(@protocol(NewTabPageControllerDelegate));
dispatcher_ = OCMProtocolMock(@protocol(NewTabPageTabDispatcher));
web_state_list_ = std::make_unique<WebStateList>(&web_state_list_delegate_);
}
NewTabPageTabHelper* tab_helper() {
return NewTabPageTabHelper::FromWebState(&test_web_state_);
}
void CreateTabHelper() {
NewTabPageTabHelper::CreateForWebState(
&test_web_state_, web_state_list_.get(), delegate_, loader_,
toolbar_delegate_, dispatcher_);
}
id dispatcher_;
id toolbar_delegate_;
id loader_;
id delegate_;
web::TestWebThreadBundle thread_bundle_;
IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
std::unique_ptr<WebStateList> web_state_list_;
FakeWebStateListDelegate web_state_list_delegate_;
std::unique_ptr<web::NavigationItem> pending_item_;
web::TestNavigationManager* test_navigation_manager_;
web::TestWebState test_web_state_;
};
// Tests a newly created NTP webstate.
TEST_F(NewTabPageTabHelperTest, TestAlreadyNTP) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitAndEnableFeature(kBrowserContainerContainsNTP);
GURL url(kChromeUINewTabURL);
test_web_state_.SetVisibleURL(url);
CreateTabHelper();
EXPECT_TRUE(tab_helper()->IsActive());
}
// Tests a newly created non-NTP webstate.
TEST_F(NewTabPageTabHelperTest, TestNotNTP) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitAndEnableFeature(kBrowserContainerContainsNTP);
GURL url(kTestURL);
test_web_state_.SetVisibleURL(url);
CreateTabHelper();
EXPECT_FALSE(tab_helper()->IsActive());
}
// Tests navigating back and forth between an NTP and non-NTP page.
TEST_F(NewTabPageTabHelperTest, TestToggleToAndFromNTP) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitAndEnableFeature(kBrowserContainerContainsNTP);
CreateTabHelper();
EXPECT_FALSE(tab_helper()->IsActive());
GURL url(kChromeUINewTabURL);
web::FakeNavigationContext context;
test_navigation_manager_->GetPendingItem()->SetURL(url);
test_web_state_.OnNavigationStarted(&context);
EXPECT_TRUE(tab_helper()->IsActive());
GURL not_ntp_url(kTestURL);
test_navigation_manager_->GetPendingItem()->SetURL(not_ntp_url);
test_web_state_.OnNavigationStarted(&context);
EXPECT_FALSE(tab_helper()->IsActive());
test_navigation_manager_->GetPendingItem()->SetURL(url);
test_web_state_.OnNavigationStarted(&context);
EXPECT_TRUE(tab_helper()->IsActive());
test_navigation_manager_->GetPendingItem()->SetURL(not_ntp_url);
test_web_state_.OnNavigationStarted(&context);
EXPECT_FALSE(tab_helper()->IsActive());
}
......@@ -7,6 +7,7 @@ source_set("ntp") {
"new_tab_page_controller_delegate.h",
"new_tab_page_header_constants.h",
"new_tab_page_header_constants.mm",
"new_tab_page_owning.h",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
......@@ -14,6 +15,24 @@ source_set("ntp") {
]
}
source_set("coordinator") {
sources = [
"new_tab_page_coordinator.h",
"new_tab_page_coordinator.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":ntp",
":ntp_internal",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/ui/content_suggestions",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/web_state_list",
"//ios/public/provider/chrome/browser/voice",
"//ios/web/public",
]
}
source_set("ntp_controller") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
......@@ -150,10 +169,12 @@ source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
sources = [
"new_tab_page_coordinator_unittest.mm",
"notification_promo_whats_new_unittest.mm",
"ntp_tile_saver_unittest.mm",
]
deps = [
":coordinator",
":ntp",
":ntp_controller",
":ntp_internal",
......@@ -171,6 +192,7 @@ source_set("unit_tests") {
"//ios/chrome/browser/bookmarks",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/favicon",
"//ios/chrome/browser/ntp_snippets:ntp_snippets",
"//ios/chrome/browser/search_engines",
"//ios/chrome/browser/sessions",
"//ios/chrome/browser/sessions:test_support",
......@@ -178,6 +200,7 @@ source_set("unit_tests") {
"//ios/chrome/browser/ui",
"//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
"//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
"//ios/chrome/browser/ui/favicon",
"//ios/chrome/browser/web_state_list:test_support",
"//ios/chrome/browser/web_state_list:web_state_list",
......
......@@ -16,7 +16,6 @@
// Init with the given loader object. |loader| may be nil, but isn't
// retained so it must outlive this controller.
// |toolbarDelegate| is used to fade the toolbar views on page scroll.
- (id)initWithLoader:(id<UrlLoader>)loader;
@end
......
......@@ -10,6 +10,7 @@
#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
#import "ios/chrome/browser/ui/native_content_controller.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_owning.h"
#import "ios/public/provider/chrome/browser/voice/logo_animation_controller.h"
namespace ios {
......@@ -34,7 +35,8 @@ class ChromeBrowserState;
// |currentController_|.
//
@interface NewTabPageController
: NativeContentController<LogoAnimationControllerOwnerOwner,
: NativeContentController<NewTabPageOwning,
LogoAnimationControllerOwnerOwner,
UIGestureRecognizerDelegate,
UIScrollViewDelegate>
......
// 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_NTP_NEW_TAB_PAGE_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_COORDINATOR_H_
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_owning.h"
#import "ios/public/provider/chrome/browser/voice/logo_animation_controller.h"
class WebStateList;
@protocol ApplicationCommands;
@protocol BrowserCommands;
@protocol OmniboxFocuser;
@protocol FakeboxFocuser;
@protocol SnackbarCommands;
@protocol UrlLoader;
@protocol NewTabPageControllerDelegate;
// Coordinator handling the NTP.
@interface NewTabPageCoordinator
: ChromeCoordinator<NewTabPageOwning, LogoAnimationControllerOwnerOwner>
// Initializes this Coordinator with its |browserState|.
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
NS_UNAVAILABLE;
- (instancetype)initWithBaseViewController:(UIViewController*)viewController
browserState:
(ios::ChromeBrowserState*)browserState
NS_UNAVAILABLE;
// ViewController associated with this coordinator.
@property(nonatomic, strong, readonly) UIViewController* viewController;
// URL loader to pass to ContentSuggestionsCoordinator.
@property(nonatomic, weak) id<UrlLoader> URLLoader;
// The web state list to pass to ContentSuggestionsCoordinator.
@property(nonatomic, assign) WebStateList* webStateList;
// The toolbar delegate to pass to ContentSuggestionsCoordinator.
@property(nonatomic, weak) id<NewTabPageControllerDelegate> toolbarDelegate;
// The dispatcher to pass to ContentSuggestionsCoordinator.
@property(nonatomic, weak) id<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader>
dispatcher;
// Returns |YES| if the coordinator is started.
@property(nonatomic, assign, getter=isStarted) BOOL started;
// Dismisses all modals owned by the NTP.
- (void)dismissModals;
@end
#endif // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_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/ntp/new_tab_page_coordinator.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.h"
#import "ios/chrome/browser/ui/ntp/incognito_view_controller.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#import "ios/web/public/navigation_item.h"
#import "ios/web/public/navigation_manager.h"
#import "ios/web/public/web_state/navigation_context.h"
#import "ios/web/public/web_state/web_state.h"
#import "ios/web/public/web_state/web_state_observer_bridge.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface NewTabPageCoordinator ()
// Coordinator for the ContentSuggestions.
@property(nonatomic, strong)
ContentSuggestionsCoordinator* contentSuggestionsCoordinator;
// View controller for incognito.
@property(nonatomic, strong) IncognitoViewController* incognitoViewController;
@end
@implementation NewTabPageCoordinator
#pragma mark - ChromeCoordinator
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
return [super initWithBaseViewController:nil browserState:browserState];
}
- (void)start {
if (self.started)
return;
DCHECK(self.browserState);
DCHECK(self.webStateList);
DCHECK(self.dispatcher);
DCHECK(self.URLLoader);
DCHECK(self.toolbarDelegate);
if (self.browserState->IsOffTheRecord()) {
DCHECK(!self.incognitoViewController);
self.incognitoViewController =
[[IncognitoViewController alloc] initWithLoader:self.URLLoader];
} else {
DCHECK(!self.contentSuggestionsCoordinator);
self.contentSuggestionsCoordinator =
[[ContentSuggestionsCoordinator alloc] initWithBaseViewController:nil];
self.contentSuggestionsCoordinator.URLLoader = self.URLLoader;
self.contentSuggestionsCoordinator.dispatcher = self.dispatcher;
self.contentSuggestionsCoordinator.browserState = self.browserState;
self.contentSuggestionsCoordinator.webStateList = self.webStateList;
self.contentSuggestionsCoordinator.toolbarDelegate = self.toolbarDelegate;
[self.contentSuggestionsCoordinator start];
base::RecordAction(base::UserMetricsAction("MobileNTPShowMostVisited"));
}
self.started = YES;
}
- (void)stop {
if (!self.started)
return;
[self.contentSuggestionsCoordinator stop];
self.contentSuggestionsCoordinator = nil;
self.incognitoViewController = nil;
self.started = NO;
}
#pragma mark - Properties
- (UIViewController*)viewController {
if (self.browserState->IsOffTheRecord()) {
return self.incognitoViewController;
} else {
return self.contentSuggestionsCoordinator.viewController;
}
}
#pragma mark - Public Methods
- (void)dismissModals {
[self.contentSuggestionsCoordinator dismissModals];
[self.incognitoViewController dismissModals];
}
#pragma mark - NewTabPageOwning
- (UIView*)view {
return self.viewController.view;
}
- (CGPoint)scrollOffset {
return [self.contentSuggestionsCoordinator scrollOffset];
}
- (void)willUpdateSnapshot {
[self.contentSuggestionsCoordinator willUpdateSnapshot];
}
- (UIEdgeInsets)contentInset {
return self.contentSuggestionsCoordinator.viewController.collectionView
.contentInset;
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
// UIKit will adjust the contentOffset sometimes when changing the
// contentInset.bottom. We don't want the NTP to scroll, so store and re-set
// the contentOffset after setting the contentInset.
CGPoint contentOffset = self.contentSuggestionsCoordinator.viewController
.collectionView.contentOffset;
self.contentSuggestionsCoordinator.viewController.collectionView
.contentInset = contentInset;
self.contentSuggestionsCoordinator.viewController.collectionView
.contentOffset = contentOffset;
}
- (void)focusFakebox {
[self.contentSuggestionsCoordinator.headerController focusFakebox];
}
#pragma mark - LogoAnimationControllerOwnerOwner
- (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner {
return [self.contentSuggestionsCoordinator
.headerController logoAnimationControllerOwner];
}
@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/ntp/new_tab_page_coordinator.h"
#include "base/test/scoped_task_environment.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
#include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
#import "ios/chrome/browser/ui/ntp/incognito_view_controller.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h"
#import "ios/chrome/browser/ui/url_loader.h"
#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
#include "ios/chrome/browser/web_state_list/web_state_list.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@protocol NewTabPageTabDispatcher<ApplicationCommands,
BrowserCommands,
OmniboxFocuser,
FakeboxFocuser,
SnackbarCommands,
UrlLoader>
@end
// Test fixture for testing NewTabPageCoordinator class.
class NewTabPageCoordinatorTest : public PlatformTest {
protected:
NewTabPageCoordinatorTest()
: scoped_browser_state_manager_(
std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.AddTestingFactory(
ios::TemplateURLServiceFactory::GetInstance(),
ios::TemplateURLServiceFactory::GetDefaultFactory());
test_cbs_builder.AddTestingFactory(
IOSChromeContentSuggestionsServiceFactory::GetInstance(),
IOSChromeContentSuggestionsServiceFactory::GetDefaultFactory());
test_cbs_builder.AddTestingFactory(
IOSChromeLargeIconServiceFactory::GetInstance(),
IOSChromeLargeIconServiceFactory::GetDefaultFactory());
browser_state_ = test_cbs_builder.Build();
loader_ = OCMProtocolMock(@protocol(UrlLoader));
toolbar_delegate_ =
OCMProtocolMock(@protocol(NewTabPageControllerDelegate));
dispatcher_ = OCMProtocolMock(@protocol(NewTabPageTabDispatcher));
web_state_list_ = std::make_unique<WebStateList>(&web_state_list_delegate_);
}
void CreateCoordinator(bool off_the_record) {
if (off_the_record) {
ios::ChromeBrowserState* otr_state =
browser_state_->GetOffTheRecordChromeBrowserState();
coordinator_ =
[[NewTabPageCoordinator alloc] initWithBrowserState:otr_state];
} else {
coordinator_ = [[NewTabPageCoordinator alloc]
initWithBrowserState:browser_state_.get()];
}
coordinator_.URLLoader = loader_;
coordinator_.toolbarDelegate = toolbar_delegate_;
coordinator_.dispatcher = dispatcher_;
coordinator_.webStateList = web_state_list_.get();
}
id dispatcher_;
id toolbar_delegate_;
id loader_;
id delegate_;
std::unique_ptr<WebStateList> web_state_list_;
FakeWebStateListDelegate web_state_list_delegate_;
web::TestWebThreadBundle thread_bundle_;
IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
NewTabPageCoordinator* coordinator_;
};
// Tests that the coordinator vends a content suggestions VC on the record.
TEST_F(NewTabPageCoordinatorTest, StartOnTheRecord) {
CreateCoordinator(/*off_the_record=*/false);
[coordinator_ start];
UIViewController* viewController = [coordinator_ viewController];
EXPECT_TRUE(
[viewController isKindOfClass:[ContentSuggestionsViewController class]]);
}
// Tests that the coordinator vends an incognito VC off the record.
TEST_F(NewTabPageCoordinatorTest, StartOffTheRecord) {
CreateCoordinator(/*off_the_record=*/true);
[coordinator_ start];
UIViewController* viewController = [coordinator_ viewController];
EXPECT_TRUE([viewController isKindOfClass:[IncognitoViewController class]]);
}
// 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_NTP_NEW_TAB_PAGE_OWNING_H_
#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_OWNING_H_
#include <UIKit/UIKit.h>
// TODO(crbug.com/826369) Helper protocol to bridge the similarities between
// NewTabPageController and NewTabpageCoordinator. This can be removed when
// NewTabPageController is removed.
@protocol NewTabPageOwning
// Exposes content inset of contentSuggestions collectionView to ensure all of
// content is visible under the bottom toolbar.
@property(nonatomic) UIEdgeInsets contentInset;
// Animates the NTP fakebox to the focused position and focuses the real
// omnibox.
- (void)focusFakebox;
// Called when a snapshot of the content will be taken.
- (void)willUpdateSnapshot;
// The scroll offset of this native view.
- (CGPoint)scrollOffset;
// The current NTP view.
- (UIView*)view;
@end
#endif // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_OWNING_H_
......@@ -158,6 +158,7 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/metrics:unit_tests",
"//ios/chrome/browser/metrics:unit_tests_internal",
"//ios/chrome/browser/net:unit_tests",
"//ios/chrome/browser/ntp:unit_tests",
"//ios/chrome/browser/omaha:unit_tests",
"//ios/chrome/browser/passwords:unit_tests",
"//ios/chrome/browser/payments: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