Commit 49a7c574 authored by Eugene But's avatar Eugene But Committed by Commit Bot

[PassKit refactoring] 3/4 PassKitCoordinator.

This is the third refactoring CL for PassKit implementation based on
ios/web Download API. CL creates PassKitCoordinator object which serves
as PassKitTabHelperDelegate and coordinates the presentation of
PKAddPassesViewController and error infobar.

TabHelper CL: crrev.com/c/794021

Design doc: https://goto.google.com/ios-chrome-passkit

Bug: 787943
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: Ifad43d79ed161f5540715accdcc1223639c55a08
Reviewed-on: https://chromium-review.googlesource.com/794302
Commit-Queue: Eugene But <eugenebut@chromium.org>
Reviewed-by: default avatarGregory Chatzinoff <gchatz@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523200}
parent 3ce021b7
......@@ -276,6 +276,7 @@ source_set("ui_internal") {
"//ios/chrome/browser/bookmarks",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/content_suggestions",
"//ios/chrome/browser/download",
"//ios/chrome/browser/favicon",
"//ios/chrome/browser/feature_engagement",
"//ios/chrome/browser/find_in_page",
......
......@@ -63,6 +63,7 @@
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/chrome_url_util.h"
#import "ios/chrome/browser/download/pass_kit_tab_helper.h"
#include "ios/chrome/browser/experimental_flags.h"
#import "ios/chrome/browser/favicon/favicon_loader.h"
#include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h"
......@@ -139,6 +140,7 @@
#import "ios/chrome/browser/ui/dialogs/dialog_presenter.h"
#import "ios/chrome/browser/ui/dialogs/java_script_dialog_presenter_impl.h"
#import "ios/chrome/browser/ui/download/legacy_download_manager_controller.h"
#import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
#import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h"
#import "ios/chrome/browser/ui/external_file_controller.h"
#import "ios/chrome/browser/ui/external_search/external_search_coordinator.h"
......@@ -601,6 +603,9 @@ NSString* const kBrowserViewControllerSnackbarCategory =
// Coordinator for the language selection UI.
LanguageSelectionCoordinator* _languageSelectionCoordinator;
// Coordinator for the PassKit UI presentation.
PassKitCoordinator* _passKitCoordinator;
// Fake status bar view used to blend the toolbar into the status bar.
UIView* _fakeStatusBarView;
......@@ -987,6 +992,9 @@ applicationCommandEndpoint:(id<ApplicationCommands>)applicationCommandEndpoint {
_languageSelectionCoordinator.presenter =
[[VerticalAnimationContainer alloc] init];
_passKitCoordinator =
[[PassKitCoordinator alloc] initWithBaseViewController:self];
_javaScriptDialogPresenter.reset(
new JavaScriptDialogPresenterImpl(_dialogPresenter));
_webStateDelegate.reset(new web::WebStateDelegateBridge(self));
......@@ -2567,6 +2575,7 @@ bubblePresenterForFeature:(const base::Feature&)feature
RepostFormTabHelper::CreateForWebState(tab.webState, self);
NetExportTabHelper::CreateForWebState(tab.webState, self);
CaptivePortalDetectorTabHelper::CreateForWebState(tab.webState, self);
PassKitTabHelper::CreateForWebState(tab.webState, _passKitCoordinator);
// The language detection helper accepts a callback from the translate
// client, so must be created after it.
......
......@@ -7,6 +7,8 @@ source_set("download") {
sources = [
"legacy_download_manager_controller.h",
"legacy_download_manager_controller.mm",
"pass_kit_coordinator.h",
"pass_kit_coordinator.mm",
]
deps = [
"resources:download_manager_controller_xib",
......@@ -15,9 +17,12 @@ source_set("download") {
"resources:file_icon_fold",
"resources:file_icon_fold_complete",
"//base",
"//components/infobars/core",
"//components/strings",
"//ios/chrome/app/strings",
"//ios/chrome/browser",
"//ios/chrome/browser/download",
"//ios/chrome/browser/infobars",
"//ios/chrome/browser/store_kit",
"//ios/chrome/browser/ui",
"//ios/chrome/browser/ui/alert_coordinator",
......@@ -39,17 +44,27 @@ source_set("unit_tests") {
testonly = true
sources = [
"legacy_download_manager_controller_unittest.mm",
"pass_kit_coordinator_unittest.mm",
]
deps = [
":download",
"//base",
"//ios/chrome/app/strings:ios_strings_grit",
"//ios/chrome/browser",
"//ios/chrome/browser/download",
"//ios/chrome/browser/download:test_support",
"//ios/chrome/browser/infobars",
"//ios/chrome/browser/store_kit",
"//ios/chrome/browser/web:test_support",
"//ios/chrome/test:test_support",
"//ios/chrome/test/fakes",
"//ios/testing:ios_test_support",
"//ios/web/public/test",
"//ios/web/public/test/fakes",
"//net:test_support",
"//testing/gtest",
"//third_party/ocmock:ocmock",
"//ui/base",
]
}
......
// Copyright 2017 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_DOWNLOAD_PASS_KIT_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_DOWNLOAD_PASS_KIT_COORDINATOR_H_
#import <PassKit/PassKit.h>
#import "ios/chrome/browser/chrome_coordinator.h"
#import "ios/chrome/browser/download/pass_kit_tab_helper_delegate.h"
namespace web {
class WebState;
} // namespace web
// Coordinates presentation of "Add pkpass UI" and "failed to add pkpass UI".
@interface PassKitCoordinator : ChromeCoordinator<PassKitTabHelperDelegate>
// Must be set before calling |start| method. Set to null when stop method is
// called or web state is destroyed.
@property(nonatomic, nonnull) web::WebState* webState;
// If the PKPass is a valid pass, then the coordinator will present the "Add
// pkpass UI". Otherwise, the coordinator will present the "failed to add
// pkpass UI". Is set to null when the stop method is called.
@property(nonatomic, nullable) PKPass* pass;
@end
#endif // IOS_CHROME_BROWSER_UI_DOWNLOAD_PASS_KIT_COORDINATOR_H_
// Copyright 2017 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/download/pass_kit_coordinator.h"
#include <memory>
#include "components/infobars/core/infobar_manager.h"
#include "components/infobars/core/simple_alert_infobar_delegate.h"
#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state/web_state_observer_bridge.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface PassKitCoordinator ()<CRWWebStateObserver,
PKAddPassesViewControllerDelegate> {
// Present the "Add pkpass UI".
PKAddPassesViewController* _viewController;
// Allows PassKitCoordinator to observe a web state.
std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
}
@end
@implementation PassKitCoordinator
@synthesize pass = _pass;
@synthesize webState = _webState;
- (instancetype)initWithBaseViewController:(UIViewController*)viewController {
self = [super initWithBaseViewController:viewController];
if (self) {
_webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
}
return self;
}
- (void)start {
DCHECK(self.webState);
if (self.pass) {
[self presentAddPassUI];
} else {
[self presentErrorUI];
}
}
- (void)stop {
[_viewController dismissViewControllerAnimated:YES completion:nil];
_viewController = nil;
_pass = nil;
if (_webState) {
_webState->RemoveObserver(_webStateObserver.get());
_webState = nullptr;
}
}
- (void)setWebState:(web::WebState*)webState {
if (_webState) {
_webState->RemoveObserver(_webStateObserver.get());
}
_webState = webState;
if (webState) {
webState->AddObserver(_webStateObserver.get());
}
}
#pragma mark - Private
// Presents PKAddPassesViewController.
- (void)presentAddPassUI {
_viewController = [[PKAddPassesViewController alloc] initWithPass:self.pass];
_viewController.delegate = self;
[self.baseViewController presentViewController:_viewController
animated:YES
completion:nil];
}
// Presents "failed to add pkpass" infobar.
- (void)presentErrorUI {
DCHECK(InfoBarManagerImpl::FromWebState(_webState));
SimpleAlertInfoBarDelegate::Create(
InfoBarManagerImpl::FromWebState(_webState),
infobars::InfoBarDelegate::SHOW_PASSKIT_INFOBAR_ERROR_DELEGATE,
/*vector_icon=*/nullptr,
l10n_util::GetStringUTF16(IDS_IOS_GENERIC_PASSKIT_ERROR),
/*auto_expire=*/true);
// Infobar does not provide callback on dismissal.
[self stop];
}
#pragma mark - PassKitTabHelperDelegate
- (void)passKitTabHelper:(PassKitTabHelper*)tabHelper
presentDialogForPass:(PKPass*)pass
webState:(web::WebState*)webState {
self.pass = pass;
self.webState = webState;
[self start];
}
#pragma mark - PKAddPassesViewControllerDelegate
- (void)addPassesViewControllerDidFinish:
(PKAddPassesViewController*)controller {
[self stop];
}
#pragma mark - WebStateObserver
- (void)webStateDestroyed:(web::WebState*)webState {
DCHECK_EQ(webState, _webState);
webState->RemoveObserver(_webStateObserver.get());
_webState = nil;
}
@end
// Copyright 2017 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/download/pass_kit_coordinator.h"
#import <PassKit/PassKit.h>
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#import "ios/chrome/browser/download/pass_kit_tab_helper.h"
#include "ios/chrome/browser/download/pass_kit_test_util.h"
#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
#include "ios/chrome/browser/ui/ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/fakes/fake_pass_kit_tab_helper_delegate.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/testing/wait_util.h"
#import "ios/web/public/test/fakes/test_navigation_manager.h"
#import "ios/web/public/test/fakes/test_web_state.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using testing::WaitUntilConditionOrTimeout;
// Test fixture for PassKitCoordinator class.
class PassKitCoordinatorTest : public PlatformTest {
protected:
PassKitCoordinatorTest()
: base_view_controller_([[UIViewController alloc] init]),
coordinator_([[PassKitCoordinator alloc]
initWithBaseViewController:base_view_controller_]),
web_state_(std::make_unique<web::TestWebState>()),
delegate_([[FakePassKitTabHelperDelegate alloc]
initWithWebState:web_state_.get()]),
test_navigation_manager_(
std::make_unique<web::TestNavigationManager>()) {
PassKitTabHelper::CreateForWebState(web_state_.get(), delegate_);
InfoBarManagerImpl::CreateForWebState(web_state_.get());
web_state_->SetNavigationManager(std::move(test_navigation_manager_));
[scoped_key_window_.Get() setRootViewController:base_view_controller_];
}
UIViewController* base_view_controller_;
PassKitCoordinator* coordinator_;
std::unique_ptr<web::TestWebState> web_state_;
FakePassKitTabHelperDelegate* delegate_;
ScopedKeyWindow scoped_key_window_;
std::unique_ptr<web::NavigationManager> test_navigation_manager_;
};
// Tests that PassKitCoordinator presents PKAddPassesViewController for the
// valid PKPass object.
TEST_F(PassKitCoordinatorTest, ValidPassKitObject) {
if (IsIPadIdiom()) {
// Wallet app is not supported on iPads.
return;
}
std::string data = testing::GetTestPass();
NSData* nsdata = [NSData dataWithBytes:data.c_str() length:data.size()];
PKPass* pass = [[PKPass alloc] initWithData:nsdata error:nil];
ASSERT_TRUE(pass);
[coordinator_
passKitTabHelper:PassKitTabHelper::FromWebState(web_state_.get())
presentDialogForPass:pass
webState:web_state_.get()];
EXPECT_TRUE(WaitUntilConditionOrTimeout(testing::kWaitForUIElementTimeout, ^{
return [base_view_controller_.presentedViewController class] ==
[PKAddPassesViewController class];
}));
[coordinator_ stop];
EXPECT_TRUE(WaitUntilConditionOrTimeout(testing::kWaitForUIElementTimeout, ^{
return base_view_controller_.presentedViewController == nil;
}));
EXPECT_FALSE(coordinator_.webState);
EXPECT_FALSE(coordinator_.pass);
}
// Tests that PassKitCoordinator presents error infobar for invalid PKPass
// object.
TEST_F(PassKitCoordinatorTest, InvalidPassKitObject) {
[coordinator_
passKitTabHelper:PassKitTabHelper::FromWebState(web_state_.get())
presentDialogForPass:nil
webState:web_state_.get()];
infobars::InfoBarManager* infobar_manager =
InfoBarManagerImpl::FromWebState(web_state_.get());
ASSERT_EQ(1U, infobar_manager->infobar_count());
infobars::InfoBar* infobar = infobar_manager->infobar_at(0);
ASSERT_TRUE(infobar->delegate());
auto* delegate = infobar->delegate()->AsConfirmInfoBarDelegate();
ASSERT_TRUE(delegate);
DCHECK_EQ(l10n_util::GetStringUTF16(IDS_IOS_GENERIC_PASSKIT_ERROR),
delegate->GetMessageText());
EXPECT_FALSE(coordinator_.webState);
EXPECT_FALSE(coordinator_.pass);
}
// Tests that destroying web state nulls out webState property.
TEST_F(PassKitCoordinatorTest, DestroyWebState) {
coordinator_.webState = web_state_.get();
ASSERT_TRUE(coordinator_.webState);
web_state_.reset();
EXPECT_FALSE(coordinator_.webState);
}
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