blob: 9aee9917438620831726daa9c9b97cd359f72fc2 [file] [log] [blame]
// Copyright 2016 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/test/app/chrome_test_util.h"
#include "base/check.h"
#import "base/ios/ios_util.h"
#include "base/mac/foundation_util.h"
#import "base/test/ios/wait_util.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
#import "components/previous_session_info/previous_session_info.h"
#import "components/previous_session_info/previous_session_info_private.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/application_delegate/metrics_mediator.h"
#import "ios/chrome/app/application_delegate/metrics_mediator_testing.h"
#import "ios/chrome/app/chrome_overlay_window.h"
#import "ios/chrome/app/main_application_delegate_testing.h"
#import "ios/chrome/app/main_controller.h"
#import "ios/chrome/app/main_controller_private.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
#import "ios/chrome/browser/main/browser.h"
#import "ios/chrome/browser/main/browser_list.h"
#import "ios/chrome/browser/main/browser_list_factory.h"
#import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
#import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
#import "ios/chrome/browser/ui/main/scene_controller.h"
#import "ios/chrome/browser/ui/main/scene_controller_testing.h"
#import "ios/chrome/browser/ui/main/scene_state.h"
#include "ios/chrome/browser/ui/util/ui_util.h"
#include "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/test/app/tab_test_util.h"
#import "ios/web/public/navigation/navigation_context.h"
#import "ios/web/public/navigation/navigation_manager.h"
#include "ios/web/public/test/fakes/fake_web_state_observer.h"
#include "net/base/mac/url_conversions.h"
#import "third_party/breakpad/breakpad/src/client/ios/BreakpadController.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Methods to access private members for testing.
@interface BreakpadController (Testing)
- (BOOL)isEnabled;
- (BOOL)isUploadingEnabled;
- (dispatch_queue_t)queue;
@end
@implementation BreakpadController (Testing)
- (BOOL)isEnabled {
return started_;
}
- (BOOL)isUploadingEnabled {
return enableUploads_;
}
- (dispatch_queue_t)queue {
return queue_;
}
@end
// A subclass to pass instances of UIOpenURLContext to scene delegate during
// testing. UIOpenURLContext has no init available, so this can only be
// allocated. It uses obscuring properties for URL and options.
// TODO(crbug.com/1115018) Explore improving this which can become brittle.
API_AVAILABLE(ios(13.0)) @interface FakeUIOpenURLContext : UIOpenURLContext
@property(nonatomic, copy) NSURL* URL;
@property(nonatomic, strong) UISceneOpenURLOptions* options;
@end
@implementation FakeUIOpenURLContext
@synthesize URL = _URL;
@synthesize options = _options;
@end
namespace {
// Returns the original ChromeBrowserState if |incognito| is false. If
// |ingonito| is true, returns an off-the-record ChromeBrowserState.
ChromeBrowserState* GetBrowserState(bool incognito) {
std::vector<ChromeBrowserState*> browser_states =
GetApplicationContext()
->GetChromeBrowserStateManager()
->GetLoadedBrowserStates();
DCHECK(!browser_states.empty());
ChromeBrowserState* browser_state = browser_states.front();
DCHECK(!browser_state->IsOffTheRecord());
return incognito ? browser_state->GetOffTheRecordChromeBrowserState()
: browser_state;
}
} // namespace
namespace chrome_test_util {
MainController* GetMainController() {
return [MainApplicationDelegate sharedMainController];
}
SceneState* GetForegroundActiveScene() {
return MainApplicationDelegate.sharedAppState.foregroundActiveScene;
}
SceneController* GetForegroundActiveSceneController() {
return MainApplicationDelegate.sharedAppState.foregroundActiveScene
.controller;
}
NSUInteger RegularBrowserCount() {
return static_cast<NSUInteger>(
BrowserListFactory::GetForBrowserState(GetOriginalBrowserState())
->AllRegularBrowsers()
.size());
}
ChromeBrowserState* GetOriginalBrowserState() {
return GetBrowserState(false);
}
ChromeBrowserState* GetCurrentIncognitoBrowserState() {
return GetBrowserState(true);
}
Browser* GetMainBrowser() {
return GetMainController().interfaceProvider.mainInterface.browser;
}
UIViewController* GetActiveViewController() {
UIWindow* main_window = GetAnyKeyWindow();
DCHECK([main_window isKindOfClass:[ChromeOverlayWindow class]]);
UIViewController* main_view_controller = main_window.rootViewController;
// The active view controller is either the TabGridViewController or its
// presented BVC. The BVC is itself contained inside of a
// BVCContainerViewController.
UIViewController* active_view_controller =
main_view_controller.presentedViewController
? main_view_controller.presentedViewController
: main_view_controller;
if ([active_view_controller
isKindOfClass:[BVCContainerViewController class]]) {
active_view_controller =
base::mac::ObjCCastStrict<BVCContainerViewController>(
active_view_controller)
.currentBVC;
}
return active_view_controller;
}
id<ApplicationCommands, BrowserCommands> HandlerForActiveBrowser() {
return static_cast<id<ApplicationCommands, BrowserCommands>>(
GetMainBrowser()->GetCommandDispatcher());
}
void RemoveAllInfoBars() {
web::WebState* webState = GetCurrentWebState();
if (webState) {
infobars::InfoBarManager* info_bar_manager =
InfoBarManagerImpl::FromWebState(webState);
if (info_bar_manager) {
info_bar_manager->RemoveAllInfoBars(false /* animate */);
}
}
}
void ClearPresentedState(ProceduralBlock completion) {
[GetForegroundActiveSceneController()
dismissModalDialogsWithCompletion:completion
dismissOmnibox:YES];
}
void SetBooleanLocalStatePref(const char* pref_name, bool value) {
DCHECK(GetApplicationContext());
DCHECK(GetApplicationContext()->GetLocalState());
BooleanPrefMember pref;
pref.Init(pref_name, GetApplicationContext()->GetLocalState());
pref.SetValue(value);
}
void SetBooleanUserPref(ChromeBrowserState* browser_state,
const char* pref_name,
bool value) {
DCHECK(browser_state);
DCHECK(browser_state->GetPrefs());
BooleanPrefMember pref;
pref.Init(pref_name, browser_state->GetPrefs());
pref.SetValue(value);
}
void SetWWANStateTo(bool value) {
MainController* mainController = chrome_test_util::GetMainController();
net::NetworkChangeNotifier::ConnectionType connectionType =
value ? net::NetworkChangeNotifier::CONNECTION_4G
: net::NetworkChangeNotifier::CONNECTION_WIFI;
[mainController.metricsMediator connectionTypeChanged:connectionType];
}
void SetFirstLaunchStateTo(bool value) {
[[PreviousSessionInfo sharedInstance] setIsFirstSessionAfterUpgrade:value];
}
bool IsMetricsRecordingEnabled() {
DCHECK(GetApplicationContext());
DCHECK(GetApplicationContext()->GetMetricsService());
return GetApplicationContext()->GetMetricsService()->recording_active();
}
bool IsMetricsReportingEnabled() {
DCHECK(GetApplicationContext());
DCHECK(GetApplicationContext()->GetMetricsService());
return GetApplicationContext()->GetMetricsService()->reporting_active();
}
bool IsBreakpadEnabled() {
return [[BreakpadController sharedInstance] isEnabled];
}
bool IsBreakpadReportingEnabled() {
return [[BreakpadController sharedInstance] isUploadingEnabled];
}
bool IsFirstLaunchAfterUpgrade() {
return [chrome_test_util::GetMainController() isFirstLaunchAfterUpgrade];
}
void WaitForBreakpadQueue() {
dispatch_queue_t queue = [[BreakpadController sharedInstance] queue];
dispatch_barrier_sync(queue, ^{
});
}
void OpenChromeFromExternalApp(const GURL& url) {
if (base::ios::IsMultiwindowSupported()) {
if (@available(iOS 13, *)) {
UIScene* scene =
[[UIApplication sharedApplication].connectedScenes anyObject];
[scene.delegate sceneWillResignActive:scene];
// FakeUIOpenURLContext cannot be instanciated, but it is just needed
// for carrying the properties over to the scene delegate.
FakeUIOpenURLContext* context = [FakeUIOpenURLContext alloc];
context.URL = net::NSURLWithGURL(url);
NSSet<UIOpenURLContext*>* URLContexts =
[[NSSet alloc] initWithArray:@[ context ]];
[scene.delegate scene:scene openURLContexts:URLContexts];
[scene.delegate sceneDidBecomeActive:scene];
}
} else {
[[[UIApplication sharedApplication] delegate]
applicationWillResignActive:[UIApplication sharedApplication]];
[GetMainController() setStartupParametersWithURL:url];
[[[UIApplication sharedApplication] delegate]
applicationDidBecomeActive:[UIApplication sharedApplication]];
}
}
bool PurgeCachedWebViewPages() {
web::WebState* web_state = chrome_test_util::GetCurrentWebState();
const GURL last_committed_url = web_state->GetLastCommittedURL();
web_state->SetWebUsageEnabled(false);
web_state->SetWebUsageEnabled(true);
auto observer = std::make_unique<web::FakeWebStateObserver>(web_state);
web::FakeWebStateObserver* observer_ptr = observer.get();
web_state->GetNavigationManager()->LoadIfNecessary();
// The navigation triggered by LoadIfNecessary() may only start loading in the
// next run loop, if it is for a web URL. The most reliable way to detect that
// this navigation has finished is via the WebStateObserver.
return base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForPageLoadTimeout, ^{
return observer_ptr->did_finish_navigation_info() &&
observer_ptr->did_finish_navigation_info()->context &&
observer_ptr->did_finish_navigation_info()
->context->GetWebState()
->GetVisibleURL() == last_committed_url;
});
}
} // namespace chrome_test_util