| // 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 |