OLD | NEW |
(Empty) | |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/app/main_controller.h" |
| 6 |
| 7 #include <memory> |
| 8 #include <string> |
| 9 |
| 10 #import <CoreSpotlight/CoreSpotlight.h> |
| 11 #import <objc/objc.h> |
| 12 #import <objc/runtime.h> |
| 13 |
| 14 #include "base/bind.h" |
| 15 #include "base/callback_helpers.h" |
| 16 #include "base/files/file_path.h" |
| 17 #include "base/ios/block_types.h" |
| 18 #import "base/mac/bind_objc_block.h" |
| 19 #include "base/mac/bundle_locations.h" |
| 20 #include "base/mac/foundation_util.h" |
| 21 #include "base/mac/objc_property_releaser.h" |
| 22 #import "base/mac/scoped_nsobject.h" |
| 23 #include "base/macros.h" |
| 24 #include "base/path_service.h" |
| 25 #include "base/strings/sys_string_conversions.h" |
| 26 #include "base/time/time.h" |
| 27 #include "components/component_updater/component_updater_service.h" |
| 28 #include "components/content_settings/core/browser/host_content_settings_map.h" |
| 29 #include "components/metrics/metrics_pref_names.h" |
| 30 #include "components/metrics/metrics_service.h" |
| 31 #include "components/prefs/pref_change_registrar.h" |
| 32 #include "components/reading_list/core/reading_list_switches.h" |
| 33 #include "components/signin/core/browser/signin_manager.h" |
| 34 #include "components/url_formatter/url_formatter.h" |
| 35 #include "components/web_resource/web_resource_pref_names.h" |
| 36 #import "ios/chrome/app/application_delegate/app_state.h" |
| 37 #import "ios/chrome/app/application_delegate/background_activity.h" |
| 38 #import "ios/chrome/app/application_delegate/metrics_mediator.h" |
| 39 #import "ios/chrome/app/application_delegate/url_opener.h" |
| 40 #include "ios/chrome/app/application_mode.h" |
| 41 #include "ios/chrome/app/chrome_app_startup_parameters.h" |
| 42 #import "ios/chrome/app/deferred_initialization_runner.h" |
| 43 #import "ios/chrome/app/main_controller_private.h" |
| 44 #import "ios/chrome/app/memory_monitor.h" |
| 45 #import "ios/chrome/app/safe_mode_crashing_modules_config.h" |
| 46 #import "ios/chrome/app/spotlight/spotlight_manager.h" |
| 47 #import "ios/chrome/app/spotlight/spotlight_util.h" |
| 48 #include "ios/chrome/app/startup/chrome_main_starter.h" |
| 49 #include "ios/chrome/app/startup/client_registration.h" |
| 50 #include "ios/chrome/app/startup/ios_chrome_main.h" |
| 51 #include "ios/chrome/app/startup/network_stack_setup.h" |
| 52 #include "ios/chrome/app/startup/provider_registration.h" |
| 53 #include "ios/chrome/app/startup/register_experimental_settings.h" |
| 54 #include "ios/chrome/app/startup/setup_debugging.h" |
| 55 #import "ios/chrome/app/startup_tasks.h" |
| 56 #include "ios/chrome/app/tests_hook.h" |
| 57 #import "ios/chrome/browser/app_startup_parameters.h" |
| 58 #include "ios/chrome/browser/application_context.h" |
| 59 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 60 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h" |
| 61 #include "ios/chrome/browser/browser_state/chrome_browser_state_removal_controll
er.h" |
| 62 #import "ios/chrome/browser/browsing_data/browsing_data_removal_controller.h" |
| 63 #include "ios/chrome/browser/browsing_data/ios_chrome_browsing_data_remover.h" |
| 64 #include "ios/chrome/browser/callback_counter.h" |
| 65 #include "ios/chrome/browser/chrome_paths.h" |
| 66 #include "ios/chrome/browser/chrome_url_constants.h" |
| 67 #import "ios/chrome/browser/chrome_url_util.h" |
| 68 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.
h" |
| 69 #include "ios/chrome/browser/crash_loop_detection_util.h" |
| 70 #include "ios/chrome/browser/crash_report/breakpad_helper.h" |
| 71 #import "ios/chrome/browser/crash_report/crash_report_background_uploader.h" |
| 72 #import "ios/chrome/browser/crash_report/crash_restore_helper.h" |
| 73 #include "ios/chrome/browser/experimental_flags.h" |
| 74 #include "ios/chrome/browser/file_metadata_util.h" |
| 75 #import "ios/chrome/browser/first_run/first_run.h" |
| 76 #include "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h" |
| 77 #include "ios/chrome/browser/ios_chrome_io_thread.h" |
| 78 #import "ios/chrome/browser/memory/memory_debugger_manager.h" |
| 79 #include "ios/chrome/browser/metrics/first_user_action_recorder.h" |
| 80 #import "ios/chrome/browser/metrics/previous_session_info.h" |
| 81 #import "ios/chrome/browser/net/cookie_util.h" |
| 82 #include "ios/chrome/browser/net/crl_set_fetcher.h" |
| 83 #include "ios/chrome/browser/pref_names.h" |
| 84 #include "ios/chrome/browser/prefs/pref_observer_bridge.h" |
| 85 #import "ios/chrome/browser/reading_list/reading_list_download_service.h" |
| 86 #import "ios/chrome/browser/reading_list/reading_list_download_service_factory.h
" |
| 87 #include "ios/chrome/browser/search_engines/search_engines_util.h" |
| 88 #include "ios/chrome/browser/search_engines/template_url_service_factory.h" |
| 89 #import "ios/chrome/browser/share_extension/share_extension_service.h" |
| 90 #import "ios/chrome/browser/share_extension/share_extension_service_factory.h" |
| 91 #include "ios/chrome/browser/signin/authentication_service.h" |
| 92 #include "ios/chrome/browser/signin/authentication_service_factory.h" |
| 93 #include "ios/chrome/browser/signin/signin_manager_factory.h" |
| 94 #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
| 95 #import "ios/chrome/browser/tabs/tab.h" |
| 96 #import "ios/chrome/browser/tabs/tab_model.h" |
| 97 #import "ios/chrome/browser/tabs/tab_model_observer.h" |
| 98 #import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller
.h" |
| 99 #import "ios/chrome/browser/ui/authentication/signin_interaction_controller.h" |
| 100 #import "ios/chrome/browser/ui/browser_view_controller.h" |
| 101 #import "ios/chrome/browser/ui/chrome_web_view_factory.h" |
| 102 #import "ios/chrome/browser/ui/commands/clear_browsing_data_command.h" |
| 103 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" |
| 104 #import "ios/chrome/browser/ui/commands/open_url_command.h" |
| 105 #import "ios/chrome/browser/ui/commands/show_signin_command.h" |
| 106 #import "ios/chrome/browser/ui/contextual_search/contextual_search_metrics.h" |
| 107 #import "ios/chrome/browser/ui/contextual_search/touch_to_search_permissions_med
iator.h" |
| 108 #import "ios/chrome/browser/ui/downloads/download_manager_controller.h" |
| 109 #import "ios/chrome/browser/ui/first_run/first_run_util.h" |
| 110 #import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h" |
| 111 #import "ios/chrome/browser/ui/fullscreen_controller.h" |
| 112 #import "ios/chrome/browser/ui/history/history_panel_view_controller.h" |
| 113 #import "ios/chrome/browser/ui/main/browser_view_wrangler.h" |
| 114 #import "ios/chrome/browser/ui/main/main_coordinator.h" |
| 115 #import "ios/chrome/browser/ui/main/main_view_controller.h" |
| 116 #import "ios/chrome/browser/ui/orientation_limiting_navigation_controller.h" |
| 117 #import "ios/chrome/browser/ui/promos/signin_promo_view_controller.h" |
| 118 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" |
| 119 #import "ios/chrome/browser/ui/stack_view/stack_view_controller.h" |
| 120 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h" |
| 121 #import "ios/chrome/browser/ui/tabs/tab_strip_controller+tab_switcher_animation.
h" |
| 122 #include "ios/chrome/browser/ui/ui_util.h" |
| 123 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 124 #import "ios/chrome/browser/ui/util/top_view_controller.h" |
| 125 #import "ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h" |
| 126 #include "ios/chrome/browser/xcallback_parameters.h" |
| 127 #include "ios/chrome/grit/ios_strings.h" |
| 128 #include "ios/net/cookies/cookie_store_ios.h" |
| 129 #import "ios/net/crn_http_protocol_handler.h" |
| 130 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| 131 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provi
der.h" |
| 132 #import "ios/public/provider/chrome/browser/native_app_launcher/native_app_white
list_manager.h" |
| 133 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h" |
| 134 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider
.h" |
| 135 #import "ios/third_party/material_components_ios/src/components/Typography/src/M
aterialTypography.h" |
| 136 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoF
ontLoader.h" |
| 137 #include "ios/web/net/request_tracker_factory_impl.h" |
| 138 #include "ios/web/net/request_tracker_impl.h" |
| 139 #include "ios/web/net/web_http_protocol_handler_delegate.h" |
| 140 #include "ios/web/public/web_capabilities.h" |
| 141 #include "ios/web/public/web_state/web_state.h" |
| 142 #import "ios/web/public/web_view_creation_util.h" |
| 143 #include "ios/web/public/webui/web_ui_ios_controller_factory.h" |
| 144 #import "ios/web/web_state/ui/crw_web_controller.h" |
| 145 #include "mojo/edk/embedder/embedder.h" |
| 146 #import "net/base/mac/url_conversions.h" |
| 147 #include "net/url_request/url_request_context.h" |
| 148 #include "ui/base/l10n/l10n_util.h" |
| 149 |
| 150 namespace { |
| 151 |
| 152 // Preference key used to store which profile is current. |
| 153 NSString* kIncognitoCurrentKey = @"IncognitoActive"; |
| 154 |
| 155 // Constants for deferred initialization of preferences observer. |
| 156 NSString* const kPrefObserverInit = @"PrefObserverInit"; |
| 157 |
| 158 // Constants for deferring notifying the AuthenticationService of a new cold |
| 159 // start. |
| 160 NSString* const kAuthenticationServiceNotification = |
| 161 @"AuthenticationServiceNotification"; |
| 162 |
| 163 // Constants for deferring reseting the startup attempt count (to give the app |
| 164 // a little while to make sure it says alive). |
| 165 NSString* const kStartupAttemptReset = @"StartupAttempReset"; |
| 166 |
| 167 // Constants for deferring memory debugging tools startup. |
| 168 NSString* const kMemoryDebuggingToolsStartup = @"MemoryDebuggingToolsStartup"; |
| 169 |
| 170 // Constants for deferring memory monitoring startup. |
| 171 NSString* const kMemoryMonitoring = @"MemoryMonitoring"; |
| 172 |
| 173 // Constants for deferred check if it is necessary to send pings to |
| 174 // Chrome distribution related services. |
| 175 NSString* const kSendInstallPingIfNecessary = @"SendInstallPingIfNecessary"; |
| 176 |
| 177 // Constants for deferring check of native iOS apps installed. |
| 178 NSString* const kCheckNativeApps = @"CheckNativeApps"; |
| 179 |
| 180 // Constants for deferred deletion of leftover user downloaded files. |
| 181 NSString* const kDeleteDownloads = @"DeleteDownloads"; |
| 182 |
| 183 // Constants for deferred sending of queued feedback. |
| 184 NSString* const kSendQueuedFeedback = @"SendQueuedFeedback"; |
| 185 |
| 186 // Constants for deferring the deletion of pre-upgrade crash reports. |
| 187 NSString* const kCleanupCrashReports = @"CleanupCrashReports"; |
| 188 |
| 189 // Constants for deferring the deletion of old snapshots. |
| 190 NSString* const kPurgeSnapshots = @"PurgeSnapshots"; |
| 191 |
| 192 // Constants for deferring startup Spotlight bookmark indexing. |
| 193 NSString* const kStartSpotlightBookmarksIndexing = |
| 194 @"StartSpotlightBookmarksIndexing"; |
| 195 |
| 196 // Constants for deferred initialization of dynamic application shortcut items. |
| 197 NSString* const kAddApplicationShortcutItems = @"AddApplicationShortcutItems"; |
| 198 |
| 199 // Constants for deferred promo display. |
| 200 const NSTimeInterval kDisplayPromoDelay = 0.1; |
| 201 |
| 202 // A rough estimate of the expected duration of a view controller transition |
| 203 // animation. It's used to temporarily disable mutally exclusive chrome |
| 204 // commands that trigger a view controller presentation. |
| 205 const int64_t kExpectedTransitionDurationInNanoSeconds = 0.2 * NSEC_PER_SEC; |
| 206 |
| 207 // Adapted from chrome/browser/ui/browser_init.cc. |
| 208 void RegisterComponentsForUpdate() { |
| 209 component_updater::ComponentUpdateService* cus = |
| 210 GetApplicationContext()->GetComponentUpdateService(); |
| 211 DCHECK(cus); |
| 212 base::FilePath path; |
| 213 const bool success = PathService::Get(ios::DIR_USER_DATA, &path); |
| 214 DCHECK(success); |
| 215 // CRLSetFetcher attempts to load a CRL set from either the local disk or |
| 216 // network. |
| 217 GetApplicationContext()->GetCRLSetFetcher()->StartInitialLoad(cus, path); |
| 218 } |
| 219 |
| 220 // Unsynchronizes the cookie store associated to |browserState| on the IO |
| 221 // thread. |
| 222 void UnSynchronizeCookieStore(ios::ChromeBrowserState* browserState) { |
| 223 DCHECK(browserState); |
| 224 scoped_refptr<net::URLRequestContextGetter> getter = |
| 225 browserState->GetRequestContext(); |
| 226 web::WebThread::PostTask( |
| 227 web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
| 228 net::CookieStoreIOS* store = static_cast<net::CookieStoreIOS*>( |
| 229 getter->GetURLRequestContext()->cookie_store()); |
| 230 store->UnSynchronize(); |
| 231 })); |
| 232 } |
| 233 |
| 234 // Returns YES if |url| matches chrome://newtab. |
| 235 BOOL IsURLNtp(const GURL& url) { |
| 236 return UrlHasChromeScheme(url) && url.host() == kChromeUINewTabHost; |
| 237 } |
| 238 |
| 239 // Used to update the current BVC mode if a new tab is added while the stack |
| 240 // view is being dimissed. This is different than ApplicationMode in that it |
| 241 // can be set to |NONE| when not in use. |
| 242 enum class StackViewDismissalMode { NONE, NORMAL, INCOGNITO }; |
| 243 |
| 244 } // namespace |
| 245 |
| 246 // TODO(crbug.com/673904): Remove once MDFRobotoFontLoader declares it directly. |
| 247 // MDFRobotoFontLoader implicitly implements MDCTypographyFontLoading but can't |
| 248 // declare it until MDC is public. |
| 249 @interface MDFRobotoFontLoader (MDCTypography)<MDCTypographyFontLoading> |
| 250 @end |
| 251 |
| 252 @interface MainController ()<BrowserStateStorageSwitching, |
| 253 BrowsingDataRemovalControllerDelegate, |
| 254 PrefObserverDelegate, |
| 255 SettingsNavigationControllerDelegate, |
| 256 TabModelObserver, |
| 257 TabSwitcherDelegate, |
| 258 UserFeedbackDataSource> { |
| 259 IBOutlet UIWindow* _window; |
| 260 |
| 261 // Weak; owned by the ChromeBrowserProvider. |
| 262 ios::ChromeBrowserStateManager* _browserStateManager; |
| 263 |
| 264 // The object that drives the Chrome startup/shutdown logic. |
| 265 std::unique_ptr<IOSChromeMain> _chromeMain; |
| 266 |
| 267 // The ChromeBrowserState associated with the main (non-OTR) browsing mode. |
| 268 ios::ChromeBrowserState* _mainBrowserState; // Weak. |
| 269 |
| 270 // Coordinators used to run the Chrome UI; there will be one of these active |
| 271 // at any given time, usually |_mainCoordinator|. |
| 272 // Main coordinator, backing object for the property of the same name, which |
| 273 // lazily initializes on access. |
| 274 base::scoped_nsobject<MainCoordinator> _mainCoordinator; |
| 275 |
| 276 // Wrangler to handle BVC and tab model creation, access, and related logic. |
| 277 // Implements faetures exposed from this object through the |
| 278 // BrowserViewInformation protocol. |
| 279 base::scoped_nsobject<BrowserViewWrangler> _browserViewWrangler; |
| 280 |
| 281 // Parameters received at startup time when the app is launched from another |
| 282 // app. |
| 283 base::scoped_nsobject<AppStartupParameters> _startupParameters; |
| 284 |
| 285 // Whether Voice Search should be started upon tab switcher dismissal. |
| 286 BOOL _startVoiceSearchAfterTabSwitcherDismissal; |
| 287 |
| 288 // Whether the QR Scanner should be started upon tab switcher dismissal. |
| 289 BOOL _startQRScannerAfterTabSwitcherDismissal; |
| 290 |
| 291 // Navigation View controller for the settings. |
| 292 base::scoped_nsobject<SettingsNavigationController> |
| 293 _settingsNavigationController; |
| 294 |
| 295 // View controller for switching tabs. |
| 296 base::scoped_nsobject<UIViewController<TabSwitcher>> _tabSwitcherController; |
| 297 |
| 298 // Controller to display the re-authentication flow. |
| 299 base::scoped_nsobject<SigninInteractionController> |
| 300 _signinInteractionController; |
| 301 |
| 302 // The number of memory warnings that have been received in this |
| 303 // foreground session. |
| 304 int _foregroundMemoryWarningCount; |
| 305 |
| 306 // The time at which to reset the OOM crash flag in the user defaults. This |
| 307 // is used to handle receiving multiple memory warnings in short succession. |
| 308 CFAbsoluteTime _outOfMemoryResetTime; |
| 309 |
| 310 // YES while animating the dismissal of stack view. |
| 311 BOOL _dismissingStackView; |
| 312 |
| 313 // If not NONE, the current BVC should be switched to this BVC on completion |
| 314 // of stack view dismissal. |
| 315 StackViewDismissalMode _modeToDisplayOnStackViewDismissal; |
| 316 |
| 317 // If YES, the tab switcher is currently active. |
| 318 BOOL _tabSwitcherIsActive; |
| 319 |
| 320 // True if the current session began from a cold start. False if the app has |
| 321 // entered the background at least once since start up. |
| 322 BOOL _isColdStart; |
| 323 |
| 324 // Keeps track of the restore state during startup. |
| 325 base::scoped_nsobject<CrashRestoreHelper> _restoreHelper; |
| 326 |
| 327 // An object to record metrics related to the user's first action. |
| 328 std::unique_ptr<FirstUserActionRecorder> _firstUserActionRecorder; |
| 329 |
| 330 // RequestTrackerFactory to customize the behavior of the network stack. |
| 331 std::unique_ptr<web::RequestTrackerFactoryImpl> _requestTrackerFactory; |
| 332 |
| 333 // Configuration for the HTTP protocol handler. |
| 334 std::unique_ptr<web::WebHTTPProtocolHandlerDelegate> |
| 335 _httpProtocolHandlerDelegate; |
| 336 |
| 337 // True if First Run UI (terms of service & sync sign-in) is being presented |
| 338 // in a modal dialog. |
| 339 BOOL _isPresentingFirstRunUI; |
| 340 |
| 341 // The tab switcher command and the voice search commands can be sent by views |
| 342 // that reside in a different UIWindow leading to the fact that the exclusive |
| 343 // touch property will be ineffective and a command for processing both |
| 344 // commands may be sent in the same run of the runloop leading to |
| 345 // inconsistencies. Those two boolean indicate if one of those commands have |
| 346 // been processed in the last 200ms in order to only allow processing one at |
| 347 // a time. |
| 348 // TODO(crbug.com/560296): Provide a general solution for handling mutually |
| 349 // exclusive chrome commands sent at nearly the same time. |
| 350 BOOL _isProcessingTabSwitcherCommand; |
| 351 BOOL _isProcessingVoiceSearchCommand; |
| 352 |
| 353 // Bridge to listen to pref changes. |
| 354 std::unique_ptr<PrefObserverBridge> _localStatePrefObserverBridge; |
| 355 |
| 356 // Registrar for pref changes notifications to the local state. |
| 357 PrefChangeRegistrar _localStatePrefChangeRegistrar; |
| 358 |
| 359 // Clears browsing data from ChromeBrowserStates. |
| 360 base::scoped_nsobject<BrowsingDataRemovalController> |
| 361 _browsingDataRemovalController; |
| 362 |
| 363 // The class in charge of showing/hiding the memory debugger when the |
| 364 // appropriate pref changes. |
| 365 base::scoped_nsobject<MemoryDebuggerManager> _memoryDebuggerManager; |
| 366 |
| 367 base::mac::ObjCPropertyReleaser _propertyReleaser_MainController; |
| 368 |
| 369 // Responsible for indexing chrome links (such as bookmarks, most likely...) |
| 370 // in system Spotlight index. |
| 371 base::scoped_nsobject<SpotlightManager> _spotlightManager; |
| 372 |
| 373 // Cached launchOptions from -didFinishLaunchingWithOptions. |
| 374 base::scoped_nsobject<NSDictionary> _launchOptions; |
| 375 |
| 376 // View controller for displaying the history panel. |
| 377 base::scoped_nsobject<UIViewController> _historyPanelViewController; |
| 378 |
| 379 // Variable backing metricsMediator property. |
| 380 base::WeakNSObject<MetricsMediator> _metricsMediator; |
| 381 |
| 382 // Hander for the startup tasks, deferred or not. |
| 383 base::scoped_nsobject<StartupTasks> _startupTasks; |
| 384 } |
| 385 |
| 386 // Pointer to the main view controller, always owned by the main window. |
| 387 @property(nonatomic, readonly) MainViewController* mainViewController; |
| 388 |
| 389 // The main coordinator, lazily created the first time it is accessed. Manages |
| 390 // the MainViewController. This property should not be accessed before the |
| 391 // browser has started up to the FOREGROUND stage. |
| 392 @property(nonatomic, readonly) MainCoordinator* mainCoordinator; |
| 393 |
| 394 // A property to track whether the QR Scanner should be started upon tab |
| 395 // switcher dismissal. It can only be YES if the QR Scanner experiment is |
| 396 // enabled. |
| 397 @property(nonatomic, readwrite) BOOL startQRScannerAfterTabSwitcherDismissal; |
| 398 |
| 399 // Activates browsing and enables web views if |enabled| is YES. |
| 400 // Disables browsing and purges web views if |enabled| is NO. |
| 401 // Must be called only on the main thread. |
| 402 - (void)setWebUsageEnabled:(BOOL)enabled; |
| 403 // Activates self.currentBVC iff the self.currentBVC can be made active. |
| 404 - (void)activateCurrentBVC; |
| 405 // Sets |currentBVC| as the root view controller for the window. |
| 406 - (void)displayCurrentBVC; |
| 407 // Shows the settings UI. |
| 408 - (void)showSettings; |
| 409 // Shows the accounts settings UI. |
| 410 - (void)showAccountsSettings; |
| 411 // Shows the Sync settings UI. |
| 412 - (void)showSyncSettings; |
| 413 // Shows the Save Passwords settings. |
| 414 - (void)showSavePasswordsSettings; |
| 415 // Invokes the sign in flow with the specified authentication operation and |
| 416 // invokes |callback| when finished. |
| 417 - (void)showSignInWithOperation:(AuthenticationOperation)operation |
| 418 signInAccessPoint:(signin_metrics::AccessPoint)signInAccessPoint |
| 419 callback:(ShowSigninCommandCompletionCallback)callback; |
| 420 // Wraps a callback with one that first checks if sign-in was completed |
| 421 // successfully and the profile wasn't swapped before invoking. |
| 422 - (ShowSigninCommandCompletionCallback)successfulSigninCompletion: |
| 423 (ProceduralBlock)callback; |
| 424 // Shows the Sync encryption passphrase (part of Settings). |
| 425 - (void)showSyncEncryptionPassphrase; |
| 426 // Shows the Native Apps Settings UI (part of Settings). |
| 427 - (void)showNativeAppsSettings; |
| 428 // Shows the Clear Browsing Data Settings UI (part of Settings). |
| 429 - (void)showClearBrowsingDataSettingsController; |
| 430 // Shows the Contextual search UI (part of Settings). |
| 431 - (void)showContextualSearchSettingsController; |
| 432 // Shows the tab switcher UI. |
| 433 - (void)showTabSwitcher; |
| 434 // Starts a voice search on the current BVC. |
| 435 - (void)startVoiceSearch; |
| 436 // Dismisses the tab switcher UI without animation into the given model. |
| 437 - (void)dismissTabSwitcherWithoutAnimationInModel:(TabModel*)tabModel; |
| 438 // Dismisses and clears |signinInteractionController|. |
| 439 - (void)dismissSigninInteractionController; |
| 440 // Called when the last incognito tab was closed. |
| 441 - (void)lastIncognitoTabClosed; |
| 442 // Called when the last regular tab was closed. |
| 443 - (void)lastRegularTabClosed; |
| 444 // Post a notification with name |notificationName| on the first available |
| 445 // run loop cycle. |
| 446 - (void)postNotificationOnNextRunLoopCycle:(NSString*)notificationName; |
| 447 // Opens a tab in the target BVC, and switches to it in a way that's appropriate |
| 448 // to the current UI, based on the |dismissModals| flag: |
| 449 // - If a modal dialog is showing and |dismissModals| is NO, the selected tab of |
| 450 // the main tab model will change in the background, but the view won't change. |
| 451 // - Otherwise, any modal view will be dismissed, the stack view will animate |
| 452 // out if it is showing, the target BVC will become active, and the new tab will |
| 453 // be shown. |
| 454 // If the current tab in |targetMode| is a NTP, it can be reused to open URL. |
| 455 - (Tab*)openSelectedTabInMode:(ApplicationMode)targetMode |
| 456 withURL:(const GURL&)url |
| 457 transition:(ui::PageTransition)transition; |
| 458 // Checks the target BVC's current tab's URL. If this URL is chrome://newtab, |
| 459 // loads |url| in this tab. Otherwise, open |url| in a new tab in the target |
| 460 // BVC. |
| 461 - (Tab*)openOrReuseTabInMode:(ApplicationMode)targetMode |
| 462 withURL:(const GURL&)url |
| 463 transition:(ui::PageTransition)transition; |
| 464 // Returns whether the restore infobar should be displayed. |
| 465 - (bool)mustShowRestoreInfobar; |
| 466 // Begins the process of dismissing the stack view with the given current model, |
| 467 // switching which BVC is suspended if necessary, but not updating the UI. |
| 468 - (void)beginDismissingStackViewWithCurrentModel:(TabModel*)tabModel; |
| 469 // Completes the process of dismissing the stack view, removing it from the |
| 470 // screen and showing the appropriate BVC. |
| 471 - (void)finishDismissingStackView; |
| 472 // Sets up self.currentBVC for testing by closing existing tabs. |
| 473 - (void)setUpCurrentBVCForTesting; |
| 474 // Opens an url. |
| 475 - (void)openUrl:(OpenUrlCommand*)command; |
| 476 // Opens an url from a link in the settings UI. |
| 477 - (void)openUrlFromSettings:(OpenUrlCommand*)command; |
| 478 // Switch all global states for the given mode (normal or incognito). |
| 479 - (void)switchGlobalStateToMode:(ApplicationMode)mode; |
| 480 // Updates the local storage, cookie store, and sets the global state. |
| 481 - (void)changeStorageFromBrowserState:(ios::ChromeBrowserState*)oldState |
| 482 toBrowserState:(ios::ChromeBrowserState*)newState; |
| 483 // Returns the set of the sessions ids of the tabs in the given |tabModel|. |
| 484 - (NSMutableSet*)liveSessionsForTabModel:(TabModel*)tabModel; |
| 485 // Purge the unused snapshots. |
| 486 - (void)purgeSnapshots; |
| 487 // Sets a LocalState pref marking the TOS EULA as accepted. |
| 488 - (void)markEulaAsAccepted; |
| 489 // Sends any feedback that happens to still be on local storage. |
| 490 - (void)sendQueuedFeedback; |
| 491 // Sets the iOS cookie policy to match that of the given browser state. |
| 492 - (void)setInitialCookiesPolicy:(ios::ChromeBrowserState*)browserState; |
| 493 // Called whenever an orientation change is received. |
| 494 - (void)orientationDidChange:(NSNotification*)notification; |
| 495 // Register to receive orientation change notification to update breakpad |
| 496 // report. |
| 497 - (void)registerForOrientationChangeNotifications; |
| 498 // Asynchronously creates the pref observers. |
| 499 - (void)schedulePrefObserverInitialization; |
| 500 // Asynchronously schedules a check for what other native iOS apps are currently |
| 501 // installed. |
| 502 - (void)scheduleCheckNativeApps; |
| 503 // Asynchronously schedules pings to distribution services. |
| 504 - (void)scheduleAppDistributionPings; |
| 505 // Asynchronously schedule the init of the memoryDebuggerManager. |
| 506 - (void)scheduleMemoryDebuggingTools; |
| 507 // Asynchronously kick off regular free memory checks. |
| 508 - (void)scheduleFreeMemoryMonitoring; |
| 509 // Asynchronously schedules the notification of the AuthenticationService. |
| 510 - (void)scheduleAuthenticationServiceNotification; |
| 511 // Asynchronously schedules the reset of the failed startup attempt counter. |
| 512 - (void)scheduleStartupAttemptReset; |
| 513 // Asynchronously schedules the cleanup of crash reports. |
| 514 - (void)scheduleCrashReportCleanup; |
| 515 // Asynchronously schedules the deletion of old snapshots. |
| 516 - (void)scheduleSnapshotPurge; |
| 517 // Schedules various cleanup tasks that are performed after launch. |
| 518 - (void)scheduleStartupCleanupTasks; |
| 519 // Schedules various tasks to be performed after the application becomes active. |
| 520 - (void)scheduleLowPriorityStartupTasks; |
| 521 // Schedules tasks that require a fully-functional BVC to be performed. |
| 522 - (void)scheduleTasksRequiringBVCWithBrowserState; |
| 523 // Schedules the deletion of user downloaded files that might be leftover |
| 524 // from the last time Chrome was run. |
| 525 - (void)scheduleDeleteDownloadsDirectory; |
| 526 // Returns whether or not the app can launch in incognito mode. |
| 527 - (BOOL)canLaunchInIncognito; |
| 528 // Determines which UI should be shown on startup, and shows it. |
| 529 - (void)createInitialUI:(ApplicationMode)launchMode; |
| 530 // Initializes the first run UI and presents it to the user. |
| 531 - (void)showFirstRunUI; |
| 532 // Schedules presentation of the first eligible promo found, if any. |
| 533 - (void)scheduleShowPromo; |
| 534 // Crashes the application if requested. |
| 535 - (void)crashIfRequested; |
| 536 // Returns the BrowsingDataRemovalController. Lazily creates one if necessary. |
| 537 - (BrowsingDataRemovalController*)browsingDataRemovalController; |
| 538 // Clears incognito data that is specific to iOS and won't be cleared by |
| 539 // deleting the browser state. |
| 540 - (void)clearIOSSpecificIncognitoData; |
| 541 // Deletes the incognito browser state. |
| 542 - (void)deleteIncognitoBrowserState; |
| 543 // Handles the notification that first run modal dialog UI is about to complete. |
| 544 - (void)handleFirstRunUIWillFinish; |
| 545 // Handles the notification that first run modal dialog UI completed. |
| 546 - (void)handleFirstRunUIDidFinish; |
| 547 // Performs synchronous browser state initialization steps. |
| 548 - (void)initializeBrowserState:(ios::ChromeBrowserState*)browserState; |
| 549 // Helper methods to initialize the application to a specific stage. |
| 550 // Setting |_browserInitializationStage| to a specific stage requires the |
| 551 // corresponding function to return YES. |
| 552 // Initializes the application to INITIALIZATION_STAGE_BASIC, which is the |
| 553 // minimum initialization needed in all cases. |
| 554 - (void)startUpBrowserBasicInitialization; |
| 555 // Initializes the application to INITIALIZATION_STAGE_BACKGROUND, which is |
| 556 // needed by background handlers. |
| 557 - (void)startUpBrowserBackgroundInitialization; |
| 558 // Initializes the application to INITIALIZATION_STAGE_FOREGROUND, which is |
| 559 // needed when application runs in foreground. |
| 560 - (void)startUpBrowserForegroundInitialization; |
| 561 // Swaps the UI between Incognito and normal modes. |
| 562 - (void)swapBrowserModes; |
| 563 @end |
| 564 |
| 565 @implementation MainController |
| 566 |
| 567 @synthesize appState = _appState; |
| 568 @synthesize appLaunchTime = _appLaunchTime; |
| 569 @synthesize browserInitializationStage = _browserInitializationStage; |
| 570 @synthesize window = _window; |
| 571 @synthesize isPresentingFirstRunUI = _isPresentingFirstRunUI; |
| 572 @synthesize isColdStart = _isColdStart; |
| 573 |
| 574 #pragma mark - Application lifecycle |
| 575 |
| 576 - (instancetype)init { |
| 577 if ((self = [super init])) { |
| 578 _propertyReleaser_MainController.Init(self, [MainController class]); |
| 579 _startupTasks.reset([[StartupTasks alloc] init]); |
| 580 } |
| 581 return self; |
| 582 } |
| 583 |
| 584 - (void)dealloc { |
| 585 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 586 net::HTTPProtocolHandlerDelegate::SetInstance(nullptr); |
| 587 net::RequestTracker::SetRequestTrackerFactory(nullptr); |
| 588 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 589 [super dealloc]; |
| 590 } |
| 591 |
| 592 // This function starts up to only what is needed at each stage of the |
| 593 // initialization. It is possible to continue initialization later. |
| 594 - (void)startUpBrowserToStage:(BrowserInitializationStageType)stage { |
| 595 if (_browserInitializationStage < INITIALIZATION_STAGE_BASIC && |
| 596 stage >= INITIALIZATION_STAGE_BASIC) { |
| 597 [self startUpBrowserBasicInitialization]; |
| 598 _browserInitializationStage = INITIALIZATION_STAGE_BASIC; |
| 599 } |
| 600 |
| 601 if (_browserInitializationStage < INITIALIZATION_STAGE_BACKGROUND && |
| 602 stage >= INITIALIZATION_STAGE_BACKGROUND) { |
| 603 [self startUpBrowserBackgroundInitialization]; |
| 604 _browserInitializationStage = INITIALIZATION_STAGE_BACKGROUND; |
| 605 } |
| 606 |
| 607 if (_browserInitializationStage < INITIALIZATION_STAGE_FOREGROUND && |
| 608 stage >= INITIALIZATION_STAGE_FOREGROUND) { |
| 609 // When adding a new initialization flow, consider setting |
| 610 // |_appState.userInteracted| at the appropriate time. |
| 611 DCHECK(_appState.userInteracted); |
| 612 [self startUpBrowserForegroundInitialization]; |
| 613 _browserInitializationStage = INITIALIZATION_STAGE_FOREGROUND; |
| 614 } |
| 615 } |
| 616 |
| 617 - (void)startUpBrowserBasicInitialization { |
| 618 _appLaunchTime = base::TimeTicks::Now(); |
| 619 _isColdStart = YES; |
| 620 |
| 621 [SetupDebugging setUpDebuggingOptions]; |
| 622 |
| 623 // Register all providers before calling any Chromium code. |
| 624 [ProviderRegistration registerProviders]; |
| 625 } |
| 626 |
| 627 - (void)startUpBrowserBackgroundInitialization { |
| 628 DCHECK(![self.appState isInSafeMode]); |
| 629 |
| 630 NSBundle* baseBundle = base::mac::OuterBundle(); |
| 631 base::mac::SetBaseBundleID( |
| 632 base::SysNSStringToUTF8([baseBundle bundleIdentifier]).c_str()); |
| 633 |
| 634 // Register default values for experimental settings (Application Preferences) |
| 635 // and set the "Version" key in the UserDefaults. |
| 636 [RegisterExperimentalSettings |
| 637 registerExperimentalSettingsWithUserDefaults:[NSUserDefaults |
| 638 standardUserDefaults] |
| 639 bundle:base::mac:: |
| 640 FrameworkBundle()]; |
| 641 |
| 642 // Register all clients before calling any web code. |
| 643 [ClientRegistration registerClients]; |
| 644 |
| 645 _chromeMain = [ChromeMainStarter startChromeMain]; |
| 646 |
| 647 // Initialize the ChromeBrowserProvider. |
| 648 ios::GetChromeBrowserProvider()->Initialize(); |
| 649 |
| 650 // If the user is interacting, crashes affect the user experience. Start |
| 651 // reporting as soon as possible. |
| 652 // TODO(crbug.com/507633): Call this even sooner. |
| 653 if (_appState.userInteracted) |
| 654 GetApplicationContext()->GetMetricsService()->OnAppEnterForeground(); |
| 655 |
| 656 web::WebUIIOSControllerFactory::RegisterFactory( |
| 657 ChromeWebUIIOSControllerFactory::GetInstance()); |
| 658 |
| 659 // TODO(crbug.com/546171): Audit all the following code to see if some of it |
| 660 // should move into BrowserMainParts or BrowserProcess. |
| 661 |
| 662 [NetworkStackSetup setUpChromeNetworkStack:&_requestTrackerFactory |
| 663 httpProtocolHandlerDelegate:&_httpProtocolHandlerDelegate]; |
| 664 } |
| 665 |
| 666 - (void)startUpBrowserForegroundInitialization { |
| 667 // Give tests a chance to prepare for testing. |
| 668 tests_hook::SetUpTestsIfPresent(); |
| 669 |
| 670 GetApplicationContext()->OnAppEnterForeground(); |
| 671 |
| 672 // TODO(crbug.com/546171): Audit all the following code to see if some of it |
| 673 // should move into BrowserMainParts or BrowserProcess. |
| 674 NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults]; |
| 675 |
| 676 // Although this duplicates some metrics_service startup logic also in |
| 677 // IOSChromeMain(), this call does additional work, checking for wifi-only |
| 678 // and setting up the required support structures. |
| 679 [_metricsMediator updateMetricsStateBasedOnPrefsUserTriggered:NO]; |
| 680 |
| 681 // Resets the number of crash reports that have been uploaded since the |
| 682 // previous Foreground initialization. |
| 683 [CrashReportBackgroundUploader resetReportsUploadedInBackgroundCount]; |
| 684 |
| 685 // Resets the interval stats between two background fetch as this value may be |
| 686 // obsolete. |
| 687 [BackgroundActivity foregroundStarted]; |
| 688 |
| 689 // Crash the app during startup if requested but only after we have enabled |
| 690 // uploading crash reports. |
| 691 [self crashIfRequested]; |
| 692 |
| 693 RegisterComponentsForUpdate(); |
| 694 |
| 695 if (experimental_flags::IsAlertOnBackgroundUploadEnabled()) { |
| 696 if ([UIApplication instancesRespondToSelector: |
| 697 @selector(registerUserNotificationSettings:)]) { |
| 698 [[UIApplication sharedApplication] |
| 699 registerUserNotificationSettings: |
| 700 [UIUserNotificationSettings |
| 701 settingsForTypes:UIUserNotificationTypeAlert | |
| 702 UIUserNotificationTypeBadge | |
| 703 UIUserNotificationTypeSound |
| 704 categories:nil]]; |
| 705 } |
| 706 } |
| 707 |
| 708 // Remove the extra browser states as Chrome iOS is single profile in M48+. |
| 709 ChromeBrowserStateRemovalController::GetInstance() |
| 710 ->RemoveBrowserStatesIfNecessary(); |
| 711 |
| 712 _browserStateManager = |
| 713 GetApplicationContext()->GetChromeBrowserStateManager(); |
| 714 ios::ChromeBrowserState* chromeBrowserState = |
| 715 _browserStateManager->GetLastUsedBrowserState(); |
| 716 |
| 717 // The CrashRestoreHelper must clean up the old browser state information |
| 718 // before the tabModels can be created. |_restoreHelper| must be kept alive |
| 719 // until the BVC receives the browser state and tab model. |
| 720 BOOL postCrashLaunch = [self mustShowRestoreInfobar]; |
| 721 if (postCrashLaunch) { |
| 722 _restoreHelper.reset( |
| 723 [[CrashRestoreHelper alloc] initWithBrowserState:chromeBrowserState]); |
| 724 [_restoreHelper moveAsideSessionInformation]; |
| 725 } |
| 726 |
| 727 // Initialize and set the main browser state. |
| 728 [self initializeBrowserState:chromeBrowserState]; |
| 729 _mainBrowserState = chromeBrowserState; |
| 730 _browserViewWrangler.reset([[BrowserViewWrangler alloc] |
| 731 initWithBrowserState:_mainBrowserState |
| 732 tabModelObserver:self]); |
| 733 // Ensure the main tab model is created. |
| 734 ignore_result([_browserViewWrangler mainTabModel]); |
| 735 |
| 736 [self createSpotlightManager]; |
| 737 |
| 738 if (reading_list::switches::IsReadingListEnabled()) { |
| 739 ShareExtensionService* service = |
| 740 ShareExtensionServiceFactory::GetForBrowserState(_mainBrowserState); |
| 741 service->Initialize(); |
| 742 } |
| 743 |
| 744 // Before bringing up the UI, make sure the launch mode is correct, and |
| 745 // check for previous crashes. |
| 746 BOOL startInIncognito = [standardDefaults boolForKey:kIncognitoCurrentKey]; |
| 747 BOOL switchFromIncognito = startInIncognito && ![self canLaunchInIncognito]; |
| 748 |
| 749 if (postCrashLaunch || switchFromIncognito) { |
| 750 [self clearIOSSpecificIncognitoData]; |
| 751 if (switchFromIncognito) |
| 752 [self switchGlobalStateToMode:ApplicationMode::NORMAL]; |
| 753 } |
| 754 if (switchFromIncognito) |
| 755 startInIncognito = NO; |
| 756 |
| 757 [self createInitialUI:(startInIncognito ? ApplicationMode::INCOGNITO |
| 758 : ApplicationMode::NORMAL)]; |
| 759 |
| 760 [self scheduleStartupCleanupTasks]; |
| 761 [MetricsMediator logLaunchMetricsWithStartupInformation:self |
| 762 browserViewInformation:_browserViewWrangler]; |
| 763 |
| 764 [self scheduleLowPriorityStartupTasks]; |
| 765 |
| 766 [_browserViewWrangler updateDeviceSharingManager]; |
| 767 |
| 768 [MDCTypography setFontLoader:[MDFRobotoFontLoader sharedInstance]]; |
| 769 |
| 770 [self openTabFromLaunchOptions:_launchOptions |
| 771 startupInformation:self |
| 772 appState:self.appState]; |
| 773 _launchOptions.reset(); |
| 774 |
| 775 mojo::edk::Init(); |
| 776 |
| 777 if (!_startupParameters) { |
| 778 // The startup parameters may create new tabs or navigations. If the restore |
| 779 // infobar is displayed now, it may be dismissed immediately and the user |
| 780 // will never be able to restore the session. |
| 781 [_restoreHelper showRestoreIfNeeded:[self currentTabModel]]; |
| 782 _restoreHelper.reset(); |
| 783 } |
| 784 |
| 785 [self scheduleTasksRequiringBVCWithBrowserState]; |
| 786 |
| 787 // Now that everything is properly set up, run the tests. |
| 788 tests_hook::RunTestsIfPresent(); |
| 789 } |
| 790 |
| 791 - (void)initializeBrowserState:(ios::ChromeBrowserState*)browserState { |
| 792 DCHECK(!browserState->IsOffTheRecord()); |
| 793 [self setInitialCookiesPolicy:browserState]; |
| 794 search_engines::UpdateSearchEnginesIfNeeded( |
| 795 browserState->GetPrefs(), |
| 796 ios::TemplateURLServiceFactory::GetForBrowserState(browserState)); |
| 797 |
| 798 if ([TouchToSearchPermissionsMediator isTouchToSearchAvailableOnDevice]) { |
| 799 base::scoped_nsobject<TouchToSearchPermissionsMediator> |
| 800 touchToSearchPermissions([[TouchToSearchPermissionsMediator alloc] |
| 801 initWithBrowserState:browserState]); |
| 802 if (experimental_flags::IsForceResetContextualSearchEnabled()) { |
| 803 [touchToSearchPermissions setPreferenceState:TouchToSearch::UNDECIDED]; |
| 804 } |
| 805 ContextualSearch::RecordPreferenceState( |
| 806 [touchToSearchPermissions preferenceState]); |
| 807 } |
| 808 } |
| 809 |
| 810 - (void)handleFirstRunUIWillFinish { |
| 811 DCHECK(_isPresentingFirstRunUI); |
| 812 _isPresentingFirstRunUI = NO; |
| 813 [[NSNotificationCenter defaultCenter] |
| 814 removeObserver:self |
| 815 name:kChromeFirstRunUIWillFinishNotification |
| 816 object:nil]; |
| 817 |
| 818 [self markEulaAsAccepted]; |
| 819 |
| 820 if (_startupParameters.get()) { |
| 821 [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL |
| 822 withURL:[_startupParameters externalURL] |
| 823 transition:ui::PAGE_TRANSITION_LINK |
| 824 completion:nil]; |
| 825 _startupParameters.reset(); |
| 826 } |
| 827 } |
| 828 |
| 829 - (void)handleFirstRunUIDidFinish { |
| 830 [[NSNotificationCenter defaultCenter] |
| 831 removeObserver:self |
| 832 name:kChromeFirstRunUIDidFinishNotification |
| 833 object:nil]; |
| 834 |
| 835 // As soon as First Run has finished, give OmniboxGeolocationController an |
| 836 // opportunity to present the iOS system location alert. |
| 837 [[OmniboxGeolocationController sharedInstance] |
| 838 triggerSystemPromptForNewUser:YES]; |
| 839 } |
| 840 |
| 841 - (void)clearIOSSpecificIncognitoData { |
| 842 DCHECK(_mainBrowserState->HasOffTheRecordChromeBrowserState()); |
| 843 ios::ChromeBrowserState* otrBrowserState = |
| 844 _mainBrowserState->GetOffTheRecordChromeBrowserState(); |
| 845 int removeAllMask = ~0; |
| 846 [self.browsingDataRemovalController |
| 847 removeIOSSpecificIncognitoBrowsingDataFromBrowserState:otrBrowserState |
| 848 mask:removeAllMask |
| 849 completionHandler:^{ |
| 850 [self activateCurrentBVC]; |
| 851 }]; |
| 852 } |
| 853 |
| 854 - (void)deleteIncognitoBrowserState { |
| 855 BOOL otrBVCIsCurrent = (self.currentBVC == self.otrBVC); |
| 856 |
| 857 const BOOL isOnIPadWithTabSwitcherEnabled = |
| 858 IsIPadIdiom() && experimental_flags::IsTabSwitcherEnabled(); |
| 859 |
| 860 // If the current BVC is the otr BVC, then the user should be in the card |
| 861 // stack, this is not true for the iPad tab switcher. |
| 862 DCHECK(isOnIPadWithTabSwitcherEnabled || |
| 863 (!otrBVCIsCurrent || _tabSwitcherIsActive)); |
| 864 |
| 865 // We always clear the otr tab model on iPad. |
| 866 // Notify the _tabSwitcherController that its otrBVC will be destroyed. |
| 867 if (isOnIPadWithTabSwitcherEnabled || _tabSwitcherIsActive) |
| 868 [_tabSwitcherController setOtrTabModel:nil]; |
| 869 |
| 870 [_browserViewWrangler |
| 871 deleteIncognitoTabModelState:self.browsingDataRemovalController]; |
| 872 |
| 873 if (otrBVCIsCurrent) { |
| 874 [self activateCurrentBVC]; |
| 875 } |
| 876 |
| 877 // Always set the new otr tab model on iPad with tab switcher enabled. |
| 878 // Notify the _tabSwitcherController with the new otrBVC. |
| 879 if (isOnIPadWithTabSwitcherEnabled || _tabSwitcherIsActive) |
| 880 [_tabSwitcherController setOtrTabModel:self.otrTabModel]; |
| 881 } |
| 882 |
| 883 - (BrowsingDataRemovalController*)browsingDataRemovalController { |
| 884 if (!_browsingDataRemovalController) { |
| 885 _browsingDataRemovalController.reset( |
| 886 [[BrowsingDataRemovalController alloc] initWithDelegate:self]); |
| 887 } |
| 888 return _browsingDataRemovalController; |
| 889 } |
| 890 |
| 891 - (void)setWebUsageEnabled:(BOOL)enabled { |
| 892 DCHECK([NSThread isMainThread]); |
| 893 if (enabled) { |
| 894 [self activateCurrentBVC]; |
| 895 } else { |
| 896 [self.currentBVC setActive:enabled]; |
| 897 } |
| 898 } |
| 899 |
| 900 - (void)activateCurrentBVC { |
| 901 // If there are pending removal operations, the activation will be deferred |
| 902 // until the callback for |removeBrowsingDataFromBrowserState:| is received. |
| 903 if (![self.browsingDataRemovalController |
| 904 hasPendingRemovalOperations:self.currentBrowserState]) { |
| 905 [self.currentBVC setActive:YES]; |
| 906 [self.currentBVC setPrimary:YES]; |
| 907 } |
| 908 } |
| 909 |
| 910 #pragma mark - BrowserLauncher implementation. |
| 911 |
| 912 - (NSDictionary*)launchOptions { |
| 913 return _launchOptions; |
| 914 } |
| 915 |
| 916 - (void)setLaunchOptions:(NSDictionary*)launchOptions { |
| 917 _launchOptions.reset([launchOptions retain]); |
| 918 } |
| 919 |
| 920 #pragma mark - Property implementation. |
| 921 |
| 922 - (id<BrowserViewInformation>)browserViewInformation { |
| 923 return _browserViewWrangler; |
| 924 } |
| 925 |
| 926 - (AppStartupParameters*)startupParameters { |
| 927 return _startupParameters; |
| 928 } |
| 929 |
| 930 - (void)setStartupParameters:(AppStartupParameters*)startupParameters { |
| 931 _startupParameters.reset([startupParameters retain]); |
| 932 } |
| 933 |
| 934 - (MainViewController*)mainViewController { |
| 935 return self.mainCoordinator.mainViewController; |
| 936 } |
| 937 |
| 938 - (MainCoordinator*)mainCoordinator { |
| 939 if (_browserInitializationStage == INITIALIZATION_STAGE_BASIC) { |
| 940 NOTREACHED() << "mainCoordinator accessed too early in initialization."; |
| 941 return nil; |
| 942 } |
| 943 if (!_mainCoordinator) { |
| 944 // Lazily create the main coordinator. |
| 945 _mainCoordinator.reset( |
| 946 [[MainCoordinator alloc] initWithWindow:self.window]); |
| 947 } |
| 948 return _mainCoordinator; |
| 949 } |
| 950 |
| 951 - (BOOL)isFirstLaunchAfterUpgrade { |
| 952 return [[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade]; |
| 953 } |
| 954 |
| 955 - (MetricsMediator*)metricsMediator { |
| 956 return _metricsMediator; |
| 957 } |
| 958 |
| 959 - (void)setMetricsMediator:(MetricsMediator*)metricsMediator { |
| 960 _metricsMediator.reset(metricsMediator); |
| 961 } |
| 962 |
| 963 - (SettingsNavigationController*)settingsNavigationController { |
| 964 return _settingsNavigationController; |
| 965 } |
| 966 |
| 967 - (void)setSettingsNavigationController: |
| 968 (SettingsNavigationController*)settingsNavigationController { |
| 969 _settingsNavigationController.reset([settingsNavigationController retain]); |
| 970 } |
| 971 |
| 972 - (BOOL)startQRScannerAfterTabSwitcherDismissal { |
| 973 return (experimental_flags::IsQRCodeReaderEnabled() && |
| 974 _startQRScannerAfterTabSwitcherDismissal); |
| 975 } |
| 976 |
| 977 - (void)setStartQRScannerAfterTabSwitcherDismissal:(BOOL)startQRScanner { |
| 978 _startQRScannerAfterTabSwitcherDismissal = startQRScanner; |
| 979 } |
| 980 |
| 981 #pragma mark - StartupInformation implementation. |
| 982 |
| 983 - (FirstUserActionRecorder*)firstUserActionRecorder { |
| 984 return _firstUserActionRecorder.get(); |
| 985 } |
| 986 |
| 987 - (void)resetFirstUserActionRecorder { |
| 988 _firstUserActionRecorder.reset(); |
| 989 } |
| 990 |
| 991 - (void)expireFirstUserActionRecorderAfterDelay:(NSTimeInterval)delay { |
| 992 [self performSelector:@selector(expireFirstUserActionRecorder) |
| 993 withObject:nil |
| 994 afterDelay:delay]; |
| 995 } |
| 996 |
| 997 - (void)activateFirstUserActionRecorderWithBackgroundTime: |
| 998 (NSTimeInterval)backgroundTime { |
| 999 base::TimeDelta delta = base::TimeDelta::FromSeconds(backgroundTime); |
| 1000 _firstUserActionRecorder.reset(new FirstUserActionRecorder(delta)); |
| 1001 } |
| 1002 |
| 1003 - (void)stopChromeMain { |
| 1004 _chromeMain.reset(); |
| 1005 } |
| 1006 |
| 1007 - (BOOL)isTabSwitcherActive { |
| 1008 return _tabSwitcherIsActive; |
| 1009 } |
| 1010 |
| 1011 #pragma mark - BrowserViewInformation implementation. |
| 1012 |
| 1013 - (void)haltAllTabs { |
| 1014 [_browserViewWrangler haltAllTabs]; |
| 1015 } |
| 1016 |
| 1017 - (void)cleanDeviceSharingManager { |
| 1018 [_browserViewWrangler cleanDeviceSharingManager]; |
| 1019 } |
| 1020 |
| 1021 #pragma mark - BrowsingDataRemovalControllerDelegate methods |
| 1022 |
| 1023 - (void)removeExternalFilesForBrowserState: |
| 1024 (ios::ChromeBrowserState*)browserState |
| 1025 completionHandler:(ProceduralBlock)completionHandler { |
| 1026 // TODO(crbug.com/648940): Move this logic from BVC into |
| 1027 // BrowsingDataRemovalController thereby eliminating the need for |
| 1028 // BrowsingDataRemovalControllerDelegate. . |
| 1029 if (_mainBrowserState == browserState) { |
| 1030 [self.mainBVC removeExternalFilesImmediately:YES |
| 1031 completionHandler:completionHandler]; |
| 1032 } else if (completionHandler) { |
| 1033 dispatch_async(dispatch_get_main_queue(), completionHandler); |
| 1034 } |
| 1035 } |
| 1036 |
| 1037 #pragma mark - Startup tasks |
| 1038 |
| 1039 - (void)sendQueuedFeedback { |
| 1040 [[DeferredInitializationRunner sharedInstance] |
| 1041 enqueueBlockNamed:kSendQueuedFeedback |
| 1042 block:^{ |
| 1043 ios::GetChromeBrowserProvider() |
| 1044 ->GetUserFeedbackProvider() |
| 1045 ->Synchronize(); |
| 1046 }]; |
| 1047 } |
| 1048 |
| 1049 - (void)setInitialCookiesPolicy:(ios::ChromeBrowserState*)browserState { |
| 1050 DCHECK(browserState); |
| 1051 net::CookieStoreIOS::CookiePolicy policy = net::CookieStoreIOS::BLOCK; |
| 1052 |
| 1053 auto settingsFactory = |
| 1054 ios::HostContentSettingsMapFactory::GetForBrowserState(browserState); |
| 1055 DCHECK(settingsFactory); |
| 1056 ContentSetting cookieSetting = settingsFactory->GetDefaultContentSetting( |
| 1057 CONTENT_SETTINGS_TYPE_COOKIES, nullptr); |
| 1058 |
| 1059 if (!web::IsAcceptCookieControlSupported()) { |
| 1060 // Override the Accept Cookie policy as ALLOW is the only policy |
| 1061 // supported by //web. |
| 1062 policy = net::CookieStoreIOS::ALLOW; |
| 1063 if (cookieSetting == CONTENT_SETTING_BLOCK) { |
| 1064 settingsFactory->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_COOKIES, |
| 1065 CONTENT_SETTING_ALLOW); |
| 1066 } |
| 1067 } else { |
| 1068 switch (cookieSetting) { |
| 1069 case CONTENT_SETTING_ALLOW: |
| 1070 policy = net::CookieStoreIOS::ALLOW; |
| 1071 break; |
| 1072 case CONTENT_SETTING_BLOCK: |
| 1073 policy = net::CookieStoreIOS::BLOCK; |
| 1074 break; |
| 1075 default: |
| 1076 NOTREACHED() << "Unsupported cookie policy."; |
| 1077 break; |
| 1078 } |
| 1079 } |
| 1080 |
| 1081 web::WebThread::PostTask( |
| 1082 web::WebThread::IO, FROM_HERE, |
| 1083 base::Bind(&net::CookieStoreIOS::SetCookiePolicy, policy)); |
| 1084 } |
| 1085 |
| 1086 - (void)orientationDidChange:(NSNotification*)notification { |
| 1087 breakpad_helper::SetCurrentOrientation( |
| 1088 [[UIApplication sharedApplication] statusBarOrientation], |
| 1089 [[UIDevice currentDevice] orientation]); |
| 1090 } |
| 1091 |
| 1092 - (void)registerForOrientationChangeNotifications { |
| 1093 // Register to both device orientation and UI orientation did change |
| 1094 // notification as these two events may be triggered independantely. |
| 1095 [[NSNotificationCenter defaultCenter] |
| 1096 addObserver:self |
| 1097 selector:@selector(orientationDidChange:) |
| 1098 name:UIDeviceOrientationDidChangeNotification |
| 1099 object:nil]; |
| 1100 [[NSNotificationCenter defaultCenter] |
| 1101 addObserver:self |
| 1102 selector:@selector(orientationDidChange:) |
| 1103 name:UIApplicationDidChangeStatusBarOrientationNotification |
| 1104 object:nil]; |
| 1105 } |
| 1106 |
| 1107 - (void)schedulePrefObserverInitialization { |
| 1108 [[DeferredInitializationRunner sharedInstance] |
| 1109 enqueueBlockNamed:kPrefObserverInit |
| 1110 block:^{ |
| 1111 // Track changes to local state prefs. |
| 1112 _localStatePrefObserverBridge.reset( |
| 1113 new PrefObserverBridge(self)); |
| 1114 _localStatePrefChangeRegistrar.Init( |
| 1115 GetApplicationContext()->GetLocalState()); |
| 1116 _localStatePrefObserverBridge->ObserveChangesForPreference( |
| 1117 metrics::prefs::kMetricsReportingEnabled, |
| 1118 &_localStatePrefChangeRegistrar); |
| 1119 _localStatePrefObserverBridge->ObserveChangesForPreference( |
| 1120 prefs::kMetricsReportingWifiOnly, |
| 1121 &_localStatePrefChangeRegistrar); |
| 1122 |
| 1123 // Calls the onPreferenceChanged function in case there was |
| 1124 // a |
| 1125 // change to the observed preferences before the observer |
| 1126 // bridge was set up. |
| 1127 [self onPreferenceChanged:metrics::prefs:: |
| 1128 kMetricsReportingEnabled]; |
| 1129 [self onPreferenceChanged:prefs::kMetricsReportingWifiOnly]; |
| 1130 }]; |
| 1131 } |
| 1132 |
| 1133 - (void)scheduleCheckNativeApps { |
| 1134 void (^checkInstalledApps)(void) = ^{ |
| 1135 [ios::GetChromeBrowserProvider()->GetNativeAppWhitelistManager() |
| 1136 checkInstalledApps]; |
| 1137 }; |
| 1138 [[DeferredInitializationRunner sharedInstance] |
| 1139 enqueueBlockNamed:kCheckNativeApps |
| 1140 block:checkInstalledApps]; |
| 1141 } |
| 1142 |
| 1143 - (void)scheduleAppDistributionPings { |
| 1144 [[DeferredInitializationRunner sharedInstance] |
| 1145 enqueueBlockNamed:kSendInstallPingIfNecessary |
| 1146 block:^{ |
| 1147 net::URLRequestContextGetter* context = |
| 1148 _mainBrowserState->GetRequestContext(); |
| 1149 bool is_first_run = FirstRun::IsChromeFirstRun(); |
| 1150 ios::GetChromeBrowserProvider() |
| 1151 ->GetAppDistributionProvider() |
| 1152 ->ScheduleDistributionNotifications(context, |
| 1153 is_first_run); |
| 1154 }]; |
| 1155 } |
| 1156 |
| 1157 - (void)scheduleAuthenticationServiceNotification { |
| 1158 [[DeferredInitializationRunner sharedInstance] |
| 1159 enqueueBlockNamed:kAuthenticationServiceNotification |
| 1160 block:^{ |
| 1161 DCHECK(_mainBrowserState); |
| 1162 // Force an obvious initialization of the |
| 1163 // AuthenticationService. |
| 1164 // This is done for clarity purpose only, and should be |
| 1165 // removed |
| 1166 // alongside the delayed initialization. See |
| 1167 // crbug.com/464306. |
| 1168 AuthenticationServiceFactory::GetForBrowserState( |
| 1169 _mainBrowserState); |
| 1170 if (![self currentBrowserState]) { |
| 1171 // Active browser state should have been set before |
| 1172 // scheduling |
| 1173 // any authentication service notification. |
| 1174 NOTREACHED(); |
| 1175 return; |
| 1176 } |
| 1177 if ([SignedInAccountsViewController |
| 1178 shouldBePresentedForBrowserState: |
| 1179 [self currentBrowserState]]) { |
| 1180 [self |
| 1181 presentSignedInAccountsViewControllerForBrowserState: |
| 1182 [self currentBrowserState]]; |
| 1183 } |
| 1184 }]; |
| 1185 } |
| 1186 |
| 1187 - (void)scheduleStartupAttemptReset { |
| 1188 [[DeferredInitializationRunner sharedInstance] |
| 1189 enqueueBlockNamed:kStartupAttemptReset |
| 1190 block:^{ |
| 1191 crash_util::ResetFailedStartupAttemptCount(); |
| 1192 }]; |
| 1193 } |
| 1194 |
| 1195 - (void)scheduleCrashReportCleanup { |
| 1196 [[DeferredInitializationRunner sharedInstance] |
| 1197 enqueueBlockNamed:kCleanupCrashReports |
| 1198 block:^{ |
| 1199 breakpad_helper::CleanupCrashReports(); |
| 1200 }]; |
| 1201 } |
| 1202 |
| 1203 - (void)scheduleSnapshotPurge { |
| 1204 [[DeferredInitializationRunner sharedInstance] |
| 1205 enqueueBlockNamed:kPurgeSnapshots |
| 1206 block:^{ |
| 1207 [self purgeSnapshots]; |
| 1208 }]; |
| 1209 } |
| 1210 |
| 1211 - (void)scheduleStartupCleanupTasks { |
| 1212 // Cleanup crash reports if this is the first run after an update. |
| 1213 if ([self isFirstLaunchAfterUpgrade]) { |
| 1214 [self scheduleCrashReportCleanup]; |
| 1215 } |
| 1216 |
| 1217 // ClearSessionCookies() is not synchronous. |
| 1218 if (cookie_util::ShouldClearSessionCookies()) { |
| 1219 cookie_util::ClearSessionCookies( |
| 1220 _mainBrowserState->GetOriginalChromeBrowserState()); |
| 1221 if (![self.otrTabModel isEmpty]) { |
| 1222 cookie_util::ClearSessionCookies( |
| 1223 _mainBrowserState->GetOffTheRecordChromeBrowserState()); |
| 1224 } |
| 1225 } |
| 1226 |
| 1227 // If the user chooses to restore their session, some cached snapshots may |
| 1228 // be needed. Otherwise, purge the cached snapshots. |
| 1229 if (![self mustShowRestoreInfobar]) { |
| 1230 [self scheduleSnapshotPurge]; |
| 1231 } |
| 1232 } |
| 1233 |
| 1234 - (void)scheduleMemoryDebuggingTools { |
| 1235 if (experimental_flags::IsMemoryDebuggingEnabled()) { |
| 1236 [[DeferredInitializationRunner sharedInstance] |
| 1237 enqueueBlockNamed:kMemoryDebuggingToolsStartup |
| 1238 block:^{ |
| 1239 _memoryDebuggerManager.reset( |
| 1240 [[MemoryDebuggerManager alloc] |
| 1241 initWithView:self.window |
| 1242 prefs:GetApplicationContext() |
| 1243 ->GetLocalState()]); |
| 1244 }]; |
| 1245 } |
| 1246 } |
| 1247 |
| 1248 - (void)scheduleFreeMemoryMonitoring { |
| 1249 // TODO(crbug.com/649338): See if this method cannot call PostBlockingPoolTask |
| 1250 // directly instead of enqueueing a block. |
| 1251 [[DeferredInitializationRunner sharedInstance] |
| 1252 enqueueBlockNamed:kMemoryMonitoring |
| 1253 block:^{ |
| 1254 web::WebThread::PostBlockingPoolTask( |
| 1255 FROM_HERE, |
| 1256 base::Bind( |
| 1257 &ios_internal::AsynchronousFreeMemoryMonitor)); |
| 1258 }]; |
| 1259 } |
| 1260 |
| 1261 - (void)scheduleLowPriorityStartupTasks { |
| 1262 [_startupTasks initializeOmaha]; |
| 1263 [_startupTasks registerForApplicationWillResignActiveNotification]; |
| 1264 [self registerForOrientationChangeNotifications]; |
| 1265 |
| 1266 // Deferred tasks. |
| 1267 [self schedulePrefObserverInitialization]; |
| 1268 [self scheduleMemoryDebuggingTools]; |
| 1269 [_startupTasks scheduleDeferredBrowserStateInitialization:_mainBrowserState]; |
| 1270 [self scheduleAuthenticationServiceNotification]; |
| 1271 [self sendQueuedFeedback]; |
| 1272 [self scheduleSpotlightResync]; |
| 1273 [self scheduleDeleteDownloadsDirectory]; |
| 1274 [self scheduleStartupAttemptReset]; |
| 1275 [self scheduleFreeMemoryMonitoring]; |
| 1276 [self scheduleAddApplicationShortcutItems]; |
| 1277 [self scheduleAppDistributionPings]; |
| 1278 [self scheduleCheckNativeApps]; |
| 1279 } |
| 1280 |
| 1281 - (void)scheduleTasksRequiringBVCWithBrowserState { |
| 1282 if (GetApplicationContext()->WasLastShutdownClean()) |
| 1283 [self.mainBVC removeExternalFilesImmediately:NO completionHandler:nil]; |
| 1284 |
| 1285 [self scheduleShowPromo]; |
| 1286 } |
| 1287 |
| 1288 - (void)scheduleDeleteDownloadsDirectory { |
| 1289 [[DeferredInitializationRunner sharedInstance] |
| 1290 enqueueBlockNamed:kDeleteDownloads |
| 1291 block:^{ |
| 1292 [DownloadManagerController clearDownloadsDirectory]; |
| 1293 }]; |
| 1294 } |
| 1295 |
| 1296 - (void)createSpotlightManager { |
| 1297 if (spotlight::IsSpotlightAvailable()) { |
| 1298 _spotlightManager.reset([[SpotlightManager |
| 1299 spotlightManagerWithBrowserState:_mainBrowserState] retain]); |
| 1300 } |
| 1301 } |
| 1302 |
| 1303 - (void)scheduleSpotlightResync { |
| 1304 if (!_spotlightManager) { |
| 1305 return; |
| 1306 } |
| 1307 ProceduralBlock block = ^{ |
| 1308 [_spotlightManager resyncIndex]; |
| 1309 }; |
| 1310 [[DeferredInitializationRunner sharedInstance] |
| 1311 enqueueBlockNamed:kStartSpotlightBookmarksIndexing |
| 1312 block:block]; |
| 1313 } |
| 1314 |
| 1315 - (void)scheduleAddApplicationShortcutItems { |
| 1316 ProceduralBlock block = ^{ |
| 1317 if (experimental_flags::IsQRCodeReaderEnabled()) { |
| 1318 UIApplicationShortcutItem* qrScannerShortcutItem = [[ |
| 1319 [UIApplicationShortcutItem alloc] |
| 1320 initWithType:@"OpenQRScanner" |
| 1321 localizedTitle:l10n_util::GetNSString( |
| 1322 IDS_IOS_APPLICATION_SHORTCUT_QR_SCANNER_TITLE) |
| 1323 localizedSubtitle:nil |
| 1324 icon:[UIApplicationShortcutIcon |
| 1325 iconWithTemplateImageName: |
| 1326 @"quick_action_qr_scanner"] |
| 1327 userInfo:nil] autorelease]; |
| 1328 // Note: The following only affects dynamic shortcut items defined |
| 1329 // programmatically, and not static shortcut items set in the Info.plist |
| 1330 // file. |
| 1331 [[UIApplication sharedApplication] |
| 1332 setShortcutItems:@[ qrScannerShortcutItem ]]; |
| 1333 } else { |
| 1334 [[UIApplication sharedApplication] setShortcutItems:nil]; |
| 1335 } |
| 1336 }; |
| 1337 |
| 1338 [[DeferredInitializationRunner sharedInstance] |
| 1339 enqueueBlockNamed:kAddApplicationShortcutItems |
| 1340 block:block]; |
| 1341 } |
| 1342 |
| 1343 - (void)expireFirstUserActionRecorder { |
| 1344 // Clear out any scheduled calls to this method. For example, the app may have |
| 1345 // been backgrounded before the |kFirstUserActionTimeout| expired. |
| 1346 [NSObject |
| 1347 cancelPreviousPerformRequestsWithTarget:self |
| 1348 selector:@selector( |
| 1349 expireFirstUserActionRecorder) |
| 1350 object:nil]; |
| 1351 |
| 1352 if (_firstUserActionRecorder) { |
| 1353 _firstUserActionRecorder->Expire(); |
| 1354 _firstUserActionRecorder.reset(); |
| 1355 } |
| 1356 } |
| 1357 |
| 1358 - (BOOL)canLaunchInIncognito { |
| 1359 NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults]; |
| 1360 if (![standardDefaults boolForKey:kIncognitoCurrentKey]) |
| 1361 return NO; |
| 1362 // If the application crashed in incognito mode, don't stay in incognito |
| 1363 // mode, since the prompt to restore should happen in non-incognito |
| 1364 // context. |
| 1365 if ([self mustShowRestoreInfobar]) |
| 1366 return NO; |
| 1367 // If there are no incognito tabs, then ensure the app starts in normal mode, |
| 1368 // since the UI isn't supposed to ever put the user in incognito mode without |
| 1369 // any incognito tabs. |
| 1370 return ![self.otrTabModel isEmpty]; |
| 1371 } |
| 1372 |
| 1373 - (void)createInitialUI:(ApplicationMode)launchMode { |
| 1374 DCHECK(_mainBrowserState); |
| 1375 |
| 1376 // In order to correctly set the mode switch icon, we need to know how many |
| 1377 // tabs are in the other tab model. That means loading both models. They |
| 1378 // may already be loaded. |
| 1379 // TODO(crbug.com/546203): Find a way to handle this that's closer to the |
| 1380 // point where it is necessary. |
| 1381 TabModel* mainTabModel = self.mainTabModel; |
| 1382 TabModel* otrTabModel = self.otrTabModel; |
| 1383 |
| 1384 // MainCoordinator shouldn't have been initialized yet. |
| 1385 DCHECK(!_mainCoordinator); |
| 1386 |
| 1387 // Enables UI initializations to query the keyWindow's size. |
| 1388 [self.window makeKeyAndVisible]; |
| 1389 |
| 1390 // Lazy init of mainCoordinator. |
| 1391 [self.mainCoordinator start]; |
| 1392 |
| 1393 // Decide if the First Run UI needs to run. |
| 1394 BOOL firstRun = (FirstRun::IsChromeFirstRun() || |
| 1395 experimental_flags::AlwaysDisplayFirstRun()) && |
| 1396 !tests_hook::DisableFirstRun(); |
| 1397 |
| 1398 ios::ChromeBrowserState* browserState = |
| 1399 (launchMode == ApplicationMode::INCOGNITO) |
| 1400 ? _mainBrowserState->GetOffTheRecordChromeBrowserState() |
| 1401 : _mainBrowserState; |
| 1402 [self changeStorageFromBrowserState:nullptr toBrowserState:browserState]; |
| 1403 |
| 1404 TabModel* tabModel; |
| 1405 if (launchMode == ApplicationMode::INCOGNITO) { |
| 1406 tabModel = otrTabModel; |
| 1407 self.currentBVC = self.otrBVC; |
| 1408 } else { |
| 1409 tabModel = mainTabModel; |
| 1410 self.currentBVC = self.mainBVC; |
| 1411 } |
| 1412 if (_tabSwitcherIsActive) |
| 1413 [self dismissTabSwitcherWithoutAnimationInModel:self.mainTabModel]; |
| 1414 if (firstRun || [self shouldOpenNTPTabOnActivationOfTabModel:tabModel]) { |
| 1415 [self.currentBVC newTab:nil]; |
| 1416 } |
| 1417 |
| 1418 if (firstRun) { |
| 1419 [self showFirstRunUI]; |
| 1420 // Do not ever show the 'restore' infobar during first run. |
| 1421 _restoreHelper.reset(); |
| 1422 } |
| 1423 } |
| 1424 |
| 1425 - (void)showFirstRunUI { |
| 1426 // Register for notification when First Run is completed. |
| 1427 // Some initializations are held back until First Run modal dialog |
| 1428 // is dismissed. |
| 1429 [[NSNotificationCenter defaultCenter] |
| 1430 addObserver:self |
| 1431 selector:@selector(handleFirstRunUIWillFinish) |
| 1432 name:kChromeFirstRunUIWillFinishNotification |
| 1433 object:nil]; |
| 1434 [[NSNotificationCenter defaultCenter] |
| 1435 addObserver:self |
| 1436 selector:@selector(handleFirstRunUIDidFinish) |
| 1437 name:kChromeFirstRunUIDidFinishNotification |
| 1438 object:nil]; |
| 1439 |
| 1440 base::scoped_nsobject<WelcomeToChromeViewController> welcomeToChrome( |
| 1441 [[WelcomeToChromeViewController alloc] |
| 1442 initWithBrowserState:_mainBrowserState |
| 1443 tabModel:self.mainTabModel]); |
| 1444 base::scoped_nsobject<UINavigationController> navController( |
| 1445 [[OrientationLimitingNavigationController alloc] |
| 1446 initWithRootViewController:welcomeToChrome]); |
| 1447 [navController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve]; |
| 1448 CGRect appFrame = [[UIScreen mainScreen] bounds]; |
| 1449 [[navController view] setFrame:appFrame]; |
| 1450 _isPresentingFirstRunUI = YES; |
| 1451 [self.mainBVC presentViewController:navController animated:NO completion:nil]; |
| 1452 } |
| 1453 |
| 1454 - (void)crashIfRequested { |
| 1455 if (experimental_flags::IsStartupCrashEnabled()) { |
| 1456 // Flush out the value cached for |
| 1457 // ios_internal::breakpad::SetUploadingEnabled(). |
| 1458 [[NSUserDefaults standardUserDefaults] synchronize]; |
| 1459 |
| 1460 int* x = NULL; |
| 1461 *x = 0; |
| 1462 } |
| 1463 } |
| 1464 |
| 1465 #pragma mark - Promo support |
| 1466 |
| 1467 - (void)scheduleShowPromo { |
| 1468 // Don't show promos if first run is shown. (Note: This flag is only YES |
| 1469 // while the first run UI is visible. However, as this function is called |
| 1470 // immediately after the UI is shown, it's a safe check.) |
| 1471 if (_isPresentingFirstRunUI) |
| 1472 return; |
| 1473 // Don't show promos in Incognito mode. |
| 1474 if (self.currentBVC == self.otrBVC) |
| 1475 return; |
| 1476 // Don't show promos if the app was launched from a URL. |
| 1477 if (_startupParameters) |
| 1478 return; |
| 1479 |
| 1480 // This array should contain Class objects - one for each promo class. |
| 1481 // New PromoViewController subclasses should be added here. |
| 1482 // Note that ordering matters -- only the first promo in the array that |
| 1483 // returns true to +shouldBePresentedForProfile: will be shown. |
| 1484 // TODO(crbug.com/516154): Now that there's only one promo class, this |
| 1485 // implementation is overkill. |
| 1486 NSArray* possiblePromos = @[ [SigninPromoViewController class] ]; |
| 1487 for (id promoController in possiblePromos) { |
| 1488 Class promoClass = (Class)promoController; |
| 1489 DCHECK(class_isMetaClass(object_getClass(promoClass))); |
| 1490 DCHECK(class_getClassMethod(object_getClass(promoClass), |
| 1491 @selector(shouldBePresentedForBrowserState:))); |
| 1492 if ([promoClass shouldBePresentedForBrowserState:_mainBrowserState]) { |
| 1493 UIViewController* promoController = |
| 1494 [promoClass controllerToPresentForBrowserState:_mainBrowserState]; |
| 1495 |
| 1496 dispatch_after( |
| 1497 dispatch_time(DISPATCH_TIME_NOW, |
| 1498 (int64_t)(kDisplayPromoDelay * NSEC_PER_SEC)), |
| 1499 dispatch_get_main_queue(), ^{ |
| 1500 [self showPromo:promoController]; |
| 1501 }); |
| 1502 |
| 1503 break; |
| 1504 } |
| 1505 } |
| 1506 } |
| 1507 |
| 1508 - (void)showPromo:(UIViewController*)promo { |
| 1509 // Make sure we have the BVC here with a valid profile. |
| 1510 DCHECK([self.currentBVC browserState]); |
| 1511 |
| 1512 base::scoped_nsobject<OrientationLimitingNavigationController> navController( |
| 1513 [[OrientationLimitingNavigationController alloc] |
| 1514 initWithRootViewController:promo]); |
| 1515 |
| 1516 // Avoid presenting the promo if the current device orientation is not |
| 1517 // supported. The promo will be presented at a later moment, when the device |
| 1518 // orientation is supported. |
| 1519 UIInterfaceOrientation orientation = |
| 1520 [UIApplication sharedApplication].statusBarOrientation; |
| 1521 NSUInteger supportedOrientationsMask = |
| 1522 [navController supportedInterfaceOrientations]; |
| 1523 if (!((1 << orientation) & supportedOrientationsMask)) |
| 1524 return; |
| 1525 |
| 1526 [navController setModalTransitionStyle:[promo modalTransitionStyle]]; |
| 1527 [navController setNavigationBarHidden:YES]; |
| 1528 [[navController view] setFrame:[[UIScreen mainScreen] bounds]]; |
| 1529 |
| 1530 [self.mainBVC presentViewController:navController |
| 1531 animated:YES |
| 1532 completion:nil]; |
| 1533 } |
| 1534 |
| 1535 #pragma mark - chromeExecuteCommand |
| 1536 |
| 1537 - (IBAction)chromeExecuteCommand:(id)sender { |
| 1538 NSInteger command = [sender tag]; |
| 1539 |
| 1540 switch (command) { |
| 1541 case IDC_NEW_TAB: |
| 1542 [self createNewTabInBVC:self.mainBVC sender:sender]; |
| 1543 break; |
| 1544 case IDC_NEW_INCOGNITO_TAB: |
| 1545 [self createNewTabInBVC:self.otrBVC sender:sender]; |
| 1546 break; |
| 1547 case IDC_OPEN_URL: |
| 1548 [self openUrl:base::mac::ObjCCast<OpenUrlCommand>(sender)]; |
| 1549 break; |
| 1550 case IDC_SWITCH_BROWSER_MODES: |
| 1551 DCHECK(IsIPadIdiom()); |
| 1552 [self swapBrowserModes]; |
| 1553 break; |
| 1554 case IDC_OPTIONS: |
| 1555 [self showSettings]; |
| 1556 break; |
| 1557 case IDC_REPORT_AN_ISSUE: |
| 1558 dispatch_async(dispatch_get_main_queue(), ^{ |
| 1559 [self showReportAnIssue]; |
| 1560 }); |
| 1561 break; |
| 1562 case IDC_SHOW_SIGNIN_IOS: { |
| 1563 ShowSigninCommand* command = |
| 1564 base::mac::ObjCCastStrict<ShowSigninCommand>(sender); |
| 1565 if (command.operation == AUTHENTICATION_OPERATION_DISMISS) { |
| 1566 [self dismissSigninInteractionController]; |
| 1567 } else { |
| 1568 [self showSignInWithOperation:command.operation |
| 1569 signInAccessPoint:command.signInAccessPoint |
| 1570 callback:command.callback]; |
| 1571 } |
| 1572 break; |
| 1573 } |
| 1574 case IDC_SHOW_ACCOUNTS_SETTINGS: { |
| 1575 [self showAccountsSettings]; |
| 1576 break; |
| 1577 } |
| 1578 case IDC_SHOW_SYNC_SETTINGS: |
| 1579 [self showSyncSettings]; |
| 1580 break; |
| 1581 case IDC_SHOW_SYNC_PASSPHRASE_SETTINGS: |
| 1582 [self showSyncEncryptionPassphrase]; |
| 1583 break; |
| 1584 case IDC_SHOW_SAVE_PASSWORDS_SETTINGS: |
| 1585 [self showSavePasswordsSettings]; |
| 1586 break; |
| 1587 case IDC_SHOW_HISTORY: |
| 1588 [self showHistory]; |
| 1589 break; |
| 1590 case IDC_TOGGLE_TAB_SWITCHER: |
| 1591 DCHECK(!_tabSwitcherIsActive); |
| 1592 if ((!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()) && |
| 1593 !_isProcessingVoiceSearchCommand) { |
| 1594 [self showTabSwitcher]; |
| 1595 _isProcessingTabSwitcherCommand = YES; |
| 1596 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, |
| 1597 kExpectedTransitionDurationInNanoSeconds), |
| 1598 dispatch_get_main_queue(), ^{ |
| 1599 _isProcessingTabSwitcherCommand = NO; |
| 1600 }); |
| 1601 } |
| 1602 break; |
| 1603 case IDC_PRELOAD_VOICE_SEARCH: |
| 1604 [self.currentBVC chromeExecuteCommand:sender]; |
| 1605 break; |
| 1606 case IDC_VOICE_SEARCH: |
| 1607 if (!_isProcessingTabSwitcherCommand) { |
| 1608 [self startVoiceSearch]; |
| 1609 _isProcessingVoiceSearchCommand = YES; |
| 1610 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, |
| 1611 kExpectedTransitionDurationInNanoSeconds), |
| 1612 dispatch_get_main_queue(), ^{ |
| 1613 _isProcessingVoiceSearchCommand = NO; |
| 1614 }); |
| 1615 } |
| 1616 break; |
| 1617 case IDC_CLEAR_BROWSING_DATA_IOS: { |
| 1618 // Clear both the main browser state and the associated incognito |
| 1619 // browser state. |
| 1620 ClearBrowsingDataCommand* command = |
| 1621 base::mac::ObjCCastStrict<ClearBrowsingDataCommand>(sender); |
| 1622 ios::ChromeBrowserState* browserState = |
| 1623 [command browserState]->GetOriginalChromeBrowserState(); |
| 1624 int mask = [command mask]; |
| 1625 browsing_data::TimePeriod timePeriod = [command timePeriod]; |
| 1626 [self removeBrowsingDataFromBrowserState:browserState |
| 1627 mask:mask |
| 1628 timePeriod:timePeriod |
| 1629 completionHandler:nil]; |
| 1630 |
| 1631 if (mask & IOSChromeBrowsingDataRemover::REMOVE_COOKIES) { |
| 1632 base::Time beginDeleteTime = |
| 1633 browsing_data::CalculateBeginDeleteTime(timePeriod); |
| 1634 [ChromeWebViewFactory clearExternalCookies:browserState |
| 1635 fromTime:beginDeleteTime |
| 1636 toTime:base::Time::Max()]; |
| 1637 } |
| 1638 break; |
| 1639 } |
| 1640 case IDC_RESET_ALL_WEBVIEWS: |
| 1641 [self.currentBVC resetAllWebViews]; |
| 1642 break; |
| 1643 case IDC_SHOW_GOOGLE_APPS_SETTINGS: |
| 1644 [self showNativeAppsSettings]; |
| 1645 break; |
| 1646 case IDC_SHOW_CLEAR_BROWSING_DATA_SETTINGS: |
| 1647 [self showClearBrowsingDataSettingsController]; |
| 1648 break; |
| 1649 case IDC_SHOW_CONTEXTUAL_SEARCH_SETTINGS: |
| 1650 [self showContextualSearchSettingsController]; |
| 1651 break; |
| 1652 case IDC_CLOSE_MODALS: |
| 1653 [self dismissModalDialogsWithCompletion:nil]; |
| 1654 break; |
| 1655 case IDC_SHOW_ADD_ACCOUNT: |
| 1656 [self showAddAccount]; |
| 1657 break; |
| 1658 default: |
| 1659 // Unknown commands get dropped with a warning. |
| 1660 NOTREACHED() << "Unknown command id " << command; |
| 1661 LOG(WARNING) << "Unknown command id " << command; |
| 1662 break; |
| 1663 } |
| 1664 } |
| 1665 |
| 1666 #pragma mark - chromeExecuteCommand helpers |
| 1667 |
| 1668 - (void)openUrl:(OpenUrlCommand*)command { |
| 1669 if ([command fromChrome]) { |
| 1670 [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL |
| 1671 withURL:[command url] |
| 1672 transition:ui::PAGE_TRANSITION_TYPED |
| 1673 completion:nil]; |
| 1674 } else { |
| 1675 [self dismissModalDialogsWithCompletion:^{ |
| 1676 self.currentBVC = [command inIncognito] ? self.otrBVC : self.mainBVC; |
| 1677 [self.currentBVC webPageOrderedOpen:[command url] |
| 1678 referrer:[command referrer] |
| 1679 windowName:[command windowName] |
| 1680 inBackground:[command inBackground] |
| 1681 appendTo:[command appendTo]]; |
| 1682 }]; |
| 1683 } |
| 1684 } |
| 1685 |
| 1686 - (void)openUrlFromSettings:(OpenUrlCommand*)command { |
| 1687 DCHECK([command fromChrome]); |
| 1688 ProceduralBlock completion = ^{ |
| 1689 [self dismissModalsAndOpenSelectedTabInMode:ApplicationMode::NORMAL |
| 1690 withURL:[command url] |
| 1691 transition:ui::PAGE_TRANSITION_TYPED |
| 1692 completion:nil]; |
| 1693 }; |
| 1694 [self closeSettingsAnimated:YES completion:completion]; |
| 1695 } |
| 1696 |
| 1697 - (void)startVoiceSearch { |
| 1698 // If the background (non-current) BVC is playing TTS audio, call |
| 1699 // -startVoiceSearch on it to stop the TTS. |
| 1700 BrowserViewController* backgroundBVC = |
| 1701 self.mainBVC == self.currentBVC ? self.otrBVC : self.mainBVC; |
| 1702 if (backgroundBVC.playingTTS) |
| 1703 [backgroundBVC startVoiceSearch]; |
| 1704 else |
| 1705 [self.currentBVC startVoiceSearch]; |
| 1706 } |
| 1707 |
| 1708 #pragma mark - Preferences Management |
| 1709 |
| 1710 - (void)onPreferenceChanged:(const std::string&)preferenceName { |
| 1711 // Turn on or off metrics & crash reporting when either preference changes. |
| 1712 if (preferenceName == metrics::prefs::kMetricsReportingEnabled || |
| 1713 preferenceName == prefs::kMetricsReportingWifiOnly) { |
| 1714 [_metricsMediator updateMetricsStateBasedOnPrefsUserTriggered:YES]; |
| 1715 } |
| 1716 } |
| 1717 |
| 1718 #pragma mark - BrowserViewInformation properties |
| 1719 |
| 1720 - (BrowserViewController*)mainBVC { |
| 1721 DCHECK(_browserViewWrangler); |
| 1722 return [_browserViewWrangler mainBVC]; |
| 1723 } |
| 1724 |
| 1725 - (void)setMainBVC:(BrowserViewController*)mainBVC { |
| 1726 DCHECK(_browserViewWrangler); |
| 1727 [_browserViewWrangler setMainBVC:mainBVC]; |
| 1728 } |
| 1729 |
| 1730 - (TabModel*)mainTabModel { |
| 1731 DCHECK(_browserViewWrangler); |
| 1732 return [_browserViewWrangler mainTabModel]; |
| 1733 } |
| 1734 |
| 1735 - (void)setMainTabModel:(TabModel*)mainTabModel { |
| 1736 DCHECK(_browserViewWrangler); |
| 1737 [_browserViewWrangler setMainTabModel:mainTabModel]; |
| 1738 } |
| 1739 |
| 1740 - (BrowserViewController*)otrBVC { |
| 1741 DCHECK(_browserViewWrangler); |
| 1742 return [_browserViewWrangler otrBVC]; |
| 1743 } |
| 1744 |
| 1745 - (void)setOtrBVC:(BrowserViewController*)otrBVC { |
| 1746 DCHECK(_browserViewWrangler); |
| 1747 [_browserViewWrangler setOtrBVC:otrBVC]; |
| 1748 } |
| 1749 |
| 1750 - (TabModel*)otrTabModel { |
| 1751 DCHECK(_browserViewWrangler); |
| 1752 return [_browserViewWrangler otrTabModel]; |
| 1753 } |
| 1754 |
| 1755 - (void)setOtrTabModel:(TabModel*)otrTabModel { |
| 1756 DCHECK(_browserViewWrangler); |
| 1757 [_browserViewWrangler setOtrTabModel:otrTabModel]; |
| 1758 } |
| 1759 |
| 1760 - (BrowserViewController*)currentBVC { |
| 1761 DCHECK(_browserViewWrangler); |
| 1762 return [_browserViewWrangler currentBVC]; |
| 1763 } |
| 1764 |
| 1765 // Note that the current tab of |bvc| will normally be reloaded by this method. |
| 1766 // If a new tab is about to be added, call expectNewForegroundTab on the BVC |
| 1767 // first to avoid extra work and possible page load side-effects for the tab |
| 1768 // being replaced. |
| 1769 - (void)setCurrentBVC:(BrowserViewController*)bvc { |
| 1770 DCHECK(bvc != nil); |
| 1771 if (self.currentBVC == bvc) |
| 1772 return; |
| 1773 |
| 1774 DCHECK(_browserViewWrangler); |
| 1775 [_browserViewWrangler setCurrentBVC:bvc storageSwitcher:self]; |
| 1776 |
| 1777 if (!_dismissingStackView) |
| 1778 [self displayCurrentBVC]; |
| 1779 |
| 1780 // Tell the BVC that was made current that it can use the web. |
| 1781 [self activateCurrentBVC]; |
| 1782 } |
| 1783 |
| 1784 #pragma mark - Tab closure handlers |
| 1785 |
| 1786 - (void)lastIncognitoTabClosed { |
| 1787 DCHECK(_mainBrowserState->HasOffTheRecordChromeBrowserState()); |
| 1788 [self clearIOSSpecificIncognitoData]; |
| 1789 UnSynchronizeCookieStore( |
| 1790 _mainBrowserState->GetOffTheRecordChromeBrowserState()); |
| 1791 |
| 1792 // OffTheRecordProfileIOData cannot be deleted before all the requests are |
| 1793 // deleted. All of the request trackers associated with the closed OTR tabs |
| 1794 // will have posted CancelRequest calls to the IO thread by now; this just |
| 1795 // waits for those calls to run before calling |deleteIncognitoBrowserState|. |
| 1796 web::RequestTrackerImpl::RunAfterRequestsCancel(base::BindBlock(^{ |
| 1797 [self deleteIncognitoBrowserState]; |
| 1798 })); |
| 1799 |
| 1800 // a) The first condition can happen when the last incognito tab is closed |
| 1801 // from the tab switcher. |
| 1802 // b) The second condition can happen if some other code (like JS) triggers |
| 1803 // closure of tabs from the otr tab model when it's not current. |
| 1804 // Nothing to do here. The next user action (like clicking on an existing |
| 1805 // regular tab or creating a new incognito tab from the settings menu) will |
| 1806 // take care of the logic to mode switch. |
| 1807 if (_tabSwitcherIsActive || ![self.currentTabModel isOffTheRecord]) { |
| 1808 return; |
| 1809 } |
| 1810 |
| 1811 if (IsIPadIdiom()) { |
| 1812 if (experimental_flags::IsTabSwitcherEnabled()) { |
| 1813 [self showTabSwitcher]; |
| 1814 } else { |
| 1815 // Mode switch if not in regular mode. |
| 1816 [self swapBrowserModes]; |
| 1817 } |
| 1818 } else { |
| 1819 self.currentBVC = self.mainBVC; |
| 1820 if ([self.currentTabModel count] == 0U) { |
| 1821 [self showTabSwitcher]; |
| 1822 } |
| 1823 } |
| 1824 } |
| 1825 |
| 1826 - (void)lastRegularTabClosed { |
| 1827 // a) The first condition can happen when the last regular tab is closed from |
| 1828 // the tab switcher. |
| 1829 // b) The second condition can happen if some other code (like JS) triggers |
| 1830 // closure of tabs from the main tab model when the main tab model is not |
| 1831 // current. |
| 1832 // Nothing to do here. |
| 1833 if (_tabSwitcherIsActive || [self.currentTabModel isOffTheRecord]) { |
| 1834 return; |
| 1835 } |
| 1836 |
| 1837 if (IsIPadIdiom()) { |
| 1838 if (experimental_flags::IsTabSwitcherEnabled()) { |
| 1839 [self showTabSwitcher]; |
| 1840 } |
| 1841 } else { |
| 1842 [self showTabSwitcher]; |
| 1843 } |
| 1844 } |
| 1845 |
| 1846 #pragma mark - Mode Switching |
| 1847 |
| 1848 - (void)switchGlobalStateToMode:(ApplicationMode)mode { |
| 1849 const BOOL incognito = (mode == ApplicationMode::INCOGNITO); |
| 1850 // Write the state to disk of what is "active". |
| 1851 NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults]; |
| 1852 [standardDefaults setBool:incognito forKey:kIncognitoCurrentKey]; |
| 1853 // Save critical state information for switching between normal and |
| 1854 // incognito. |
| 1855 [standardDefaults synchronize]; |
| 1856 } |
| 1857 |
| 1858 - (void)changeStorageFromBrowserState:(ios::ChromeBrowserState*)oldState |
| 1859 toBrowserState:(ios::ChromeBrowserState*)newState { |
| 1860 ApplicationMode mode = newState->IsOffTheRecord() ? ApplicationMode::INCOGNITO |
| 1861 : ApplicationMode::NORMAL; |
| 1862 [self switchGlobalStateToMode:mode]; |
| 1863 } |
| 1864 |
| 1865 // Set |bvc| as the current BVC and then creates a new tab. |
| 1866 - (void)createNewTabInBVC:(BrowserViewController*)bvc sender:(id)sender { |
| 1867 DCHECK(bvc); |
| 1868 [bvc expectNewForegroundTab]; |
| 1869 self.currentBVC = bvc; |
| 1870 [self.currentBVC newTab:sender]; |
| 1871 } |
| 1872 |
| 1873 - (void)displayCurrentBVC { |
| 1874 self.mainViewController.activeViewController = self.currentBVC; |
| 1875 } |
| 1876 |
| 1877 - (TabModel*)currentTabModel { |
| 1878 return self.currentBVC.tabModel; |
| 1879 } |
| 1880 |
| 1881 - (ios::ChromeBrowserState*)currentBrowserState { |
| 1882 return self.currentBVC.browserState; |
| 1883 } |
| 1884 |
| 1885 - (void)swapBrowserModes { |
| 1886 if (self.mainBVC == self.currentBVC) |
| 1887 self.currentBVC = self.otrBVC; |
| 1888 else |
| 1889 self.currentBVC = self.mainBVC; |
| 1890 // Make sure there is at least one tab open. |
| 1891 if ([self shouldOpenNTPTabOnActivationOfTabModel:[self currentTabModel]]) |
| 1892 [self.currentBVC newTab:nil]; |
| 1893 [_browserViewWrangler updateModeToggle]; |
| 1894 } |
| 1895 |
| 1896 // NOTE: If you change this function, it may have an effect on the performance |
| 1897 // of opening the stack view. Please make sure you also change the corresponding |
| 1898 // code in StackViewControllerPerfTest::MainControllerShowTabSwitcher(). |
| 1899 - (void)showTabSwitcher { |
| 1900 BrowserViewController* currentBVC = self.currentBVC; |
| 1901 Tab* currentTab = [[currentBVC tabModel] currentTab]; |
| 1902 |
| 1903 // In order to generate the transition between the current browser view |
| 1904 // controller and the tab switcher controller it's possible that multiple |
| 1905 // screenshots of the same tab are taken. Since taking a screenshot is |
| 1906 // expensive we activate snapshot coalescing in the scope of this function |
| 1907 // which will cache the first snapshot for the tab and reuse it instead of |
| 1908 // regenerating a new one each time. |
| 1909 [currentTab setSnapshotCoalescingEnabled:YES]; |
| 1910 base::ScopedClosureRunner runner(base::BindBlock(^{ |
| 1911 [currentTab setSnapshotCoalescingEnabled:NO]; |
| 1912 })); |
| 1913 |
| 1914 if (experimental_flags::IsTabSwitcherEnabled()) |
| 1915 [currentBVC prepareToEnterTabSwitcher:nil]; |
| 1916 |
| 1917 if (!_tabSwitcherController.get()) { |
| 1918 if (IsIPadIdiom()) { |
| 1919 _tabSwitcherController.reset([[TabSwitcherController alloc] |
| 1920 initWithBrowserState:_mainBrowserState |
| 1921 mainTabModel:self.mainTabModel |
| 1922 otrTabModel:self.otrTabModel |
| 1923 activeTabModel:self.currentTabModel]); |
| 1924 } else { |
| 1925 _tabSwitcherController.reset([[StackViewController alloc] |
| 1926 initWithMainTabModel:self.mainTabModel |
| 1927 otrTabModel:self.otrTabModel |
| 1928 activeTabModel:self.currentTabModel]); |
| 1929 } |
| 1930 } else { |
| 1931 // The StackViewController is kept in memory to avoid the performance hit of |
| 1932 // loading from the nib on next showing, but clears out its card models to |
| 1933 // release memory. The tab models are required to rebuild the card stacks. |
| 1934 [_tabSwitcherController |
| 1935 restoreInternalStateWithMainTabModel:self.mainTabModel |
| 1936 otrTabModel:self.otrTabModel |
| 1937 activeTabModel:self.currentTabModel]; |
| 1938 } |
| 1939 _tabSwitcherIsActive = YES; |
| 1940 [_tabSwitcherController setDelegate:self]; |
| 1941 if (IsIPadIdiom() && experimental_flags::IsTabSwitcherEnabled()) { |
| 1942 TabSwitcherTransitionContext* transitionContext = |
| 1943 [TabSwitcherTransitionContext |
| 1944 tabSwitcherTransitionContextWithCurrent:currentBVC |
| 1945 mainBVC:self.mainBVC |
| 1946 otrBVC:self.otrBVC]; |
| 1947 [_tabSwitcherController setTransitionContext:transitionContext]; |
| 1948 self.mainViewController.activeViewController = _tabSwitcherController; |
| 1949 [_tabSwitcherController showWithSelectedTabAnimation]; |
| 1950 } else { |
| 1951 // User interaction is disabled when the stack controller is dismissed. |
| 1952 [[_tabSwitcherController view] setUserInteractionEnabled:YES]; |
| 1953 self.mainViewController.activeViewController = _tabSwitcherController; |
| 1954 [_tabSwitcherController showWithSelectedTabAnimation]; |
| 1955 } |
| 1956 } |
| 1957 |
| 1958 - (BOOL)shouldOpenNTPTabOnActivationOfTabModel:(TabModel*)tabModel { |
| 1959 if (_settingsNavigationController) { |
| 1960 return false; |
| 1961 } |
| 1962 if (_tabSwitcherIsActive) { |
| 1963 // Only attempt to dismiss the tab switcher and open a new tab if: |
| 1964 // - there are no tabs open in either tab model, and |
| 1965 // - the tab switcher controller is not directly or indirectly presenting |
| 1966 // another view controller. |
| 1967 if (![self.mainTabModel isEmpty] || ![self.otrTabModel isEmpty]) |
| 1968 return NO; |
| 1969 |
| 1970 UIViewController* viewController = [self topPresentedViewController]; |
| 1971 while (viewController) { |
| 1972 if ([viewController.presentingViewController |
| 1973 isEqual:_tabSwitcherController]) { |
| 1974 return NO; |
| 1975 } |
| 1976 viewController = viewController.presentingViewController; |
| 1977 } |
| 1978 return YES; |
| 1979 } |
| 1980 return ![tabModel count] && [tabModel browserState] && |
| 1981 ![tabModel browserState]->IsOffTheRecord(); |
| 1982 } |
| 1983 |
| 1984 #pragma mark - TabSwitching implementation. |
| 1985 |
| 1986 - (BOOL)openNewTabFromTabSwitcher { |
| 1987 if (!_tabSwitcherController) |
| 1988 return NO; |
| 1989 |
| 1990 [_tabSwitcherController |
| 1991 dismissWithNewTabAnimationToModel:self.mainTabModel |
| 1992 withURL:GURL(kChromeUINewTabURL) |
| 1993 atIndex:NSNotFound |
| 1994 transition:ui::PAGE_TRANSITION_TYPED]; |
| 1995 return YES; |
| 1996 } |
| 1997 |
| 1998 - (void)dismissTabSwitcherWithoutAnimationInModel:(TabModel*)tabModel { |
| 1999 DCHECK(_tabSwitcherIsActive); |
| 2000 DCHECK(!_dismissingStackView); |
| 2001 if ([_tabSwitcherController |
| 2002 respondsToSelector:@selector(tabSwitcherDismissWithModel: |
| 2003 animated:)]) { |
| 2004 [self dismissModalDialogsWithCompletion:nil]; |
| 2005 [_tabSwitcherController tabSwitcherDismissWithModel:tabModel animated:NO]; |
| 2006 } else { |
| 2007 [self beginDismissingStackViewWithCurrentModel:tabModel]; |
| 2008 [self finishDismissingStackView]; |
| 2009 } |
| 2010 } |
| 2011 |
| 2012 #pragma mark - TabSwitcherDelegate Implementation |
| 2013 |
| 2014 - (void)tabSwitcher:(id<TabSwitcher>)tabSwitcher |
| 2015 dismissTransitionWillStartWithActiveModel:(TabModel*)tabModel { |
| 2016 [self beginDismissingStackViewWithCurrentModel:tabModel]; |
| 2017 } |
| 2018 |
| 2019 - (void)tabSwitcherDismissTransitionDidEnd:(id<TabSwitcher>)tabSwitcher { |
| 2020 [self finishDismissingStackView]; |
| 2021 } |
| 2022 |
| 2023 - (void)beginDismissingStackViewWithCurrentModel:(TabModel*)tabModel { |
| 2024 DCHECK(experimental_flags::IsTabSwitcherEnabled() || !IsIPadIdiom()); |
| 2025 DCHECK(tabModel == self.mainTabModel || tabModel == self.otrTabModel); |
| 2026 |
| 2027 _dismissingStackView = YES; |
| 2028 // Prevent wayward touches from wreaking havoc while the stack view is being |
| 2029 // dismissed. |
| 2030 [[_tabSwitcherController view] setUserInteractionEnabled:NO]; |
| 2031 BrowserViewController* targetBVC = |
| 2032 (tabModel == self.mainTabModel) ? self.mainBVC : self.otrBVC; |
| 2033 self.currentBVC = targetBVC; |
| 2034 } |
| 2035 |
| 2036 - (void)finishDismissingStackView { |
| 2037 DCHECK(!IsIPadIdiom() || experimental_flags::IsTabSwitcherEnabled()); |
| 2038 DCHECK_EQ(self.mainViewController.activeViewController, |
| 2039 _tabSwitcherController.get()); |
| 2040 |
| 2041 if (_modeToDisplayOnStackViewDismissal == StackViewDismissalMode::NORMAL) { |
| 2042 self.currentBVC = self.mainBVC; |
| 2043 } else if (_modeToDisplayOnStackViewDismissal == |
| 2044 StackViewDismissalMode::INCOGNITO) { |
| 2045 self.currentBVC = self.otrBVC; |
| 2046 } |
| 2047 |
| 2048 _modeToDisplayOnStackViewDismissal = StackViewDismissalMode::NONE; |
| 2049 |
| 2050 // Displaying the current BVC dismisses the stack view. |
| 2051 [self displayCurrentBVC]; |
| 2052 |
| 2053 // Start Voice Search or QR Scanner now that they can be presented from the |
| 2054 // current BVC. |
| 2055 if (_startVoiceSearchAfterTabSwitcherDismissal) { |
| 2056 _startVoiceSearchAfterTabSwitcherDismissal = NO; |
| 2057 [self.currentBVC startVoiceSearch]; |
| 2058 } else if ([self startQRScannerAfterTabSwitcherDismissal]) { |
| 2059 [self setStartQRScannerAfterTabSwitcherDismissal:NO]; |
| 2060 [self.currentBVC showQRScanner]; |
| 2061 } |
| 2062 |
| 2063 [_tabSwitcherController setDelegate:nil]; |
| 2064 |
| 2065 _tabSwitcherIsActive = NO; |
| 2066 _dismissingStackView = NO; |
| 2067 } |
| 2068 |
| 2069 - (void)tabSwitcherPresentationTransitionDidEnd:(id<TabSwitcher>)tabSwitcher { |
| 2070 } |
| 2071 |
| 2072 - (id<ToolbarOwner>)tabSwitcherTransitionToolbarOwner { |
| 2073 // Request the view to ensure that the view has been loaded and initialized, |
| 2074 // since it may never have been loaded (or have been swapped out). |
| 2075 [self.currentBVC ensureViewCreated]; |
| 2076 return self.currentBVC; |
| 2077 } |
| 2078 |
| 2079 #pragma mark - Browsing data clearing |
| 2080 |
| 2081 - (void)removeBrowsingDataFromBrowserState: |
| 2082 (ios::ChromeBrowserState*)browserState |
| 2083 mask:(int)mask |
| 2084 timePeriod:(browsing_data::TimePeriod)timePeriod |
| 2085 completionHandler:(ProceduralBlock)completionHandler { |
| 2086 // TODO(crbug.com/632772): Remove web usage disabling once |
| 2087 // https://bugs.webkit.org/show_bug.cgi?id=149079 has been fixed. |
| 2088 if (browserState == self.currentBrowserState && |
| 2089 (mask & IOSChromeBrowsingDataRemover::REMOVE_COOKIES)) { |
| 2090 [self setWebUsageEnabled:NO]; |
| 2091 } |
| 2092 |
| 2093 ProceduralBlock browsingDataRemoved = ^{ |
| 2094 [self setWebUsageEnabled:YES]; |
| 2095 if (completionHandler) { |
| 2096 completionHandler(); |
| 2097 } |
| 2098 }; |
| 2099 [self.browsingDataRemovalController |
| 2100 removeBrowsingDataFromBrowserState:browserState |
| 2101 mask:mask |
| 2102 timePeriod:timePeriod |
| 2103 completionHandler:browsingDataRemoved]; |
| 2104 } |
| 2105 |
| 2106 #pragma mark - Navigation Controllers |
| 2107 |
| 2108 - (void)presentSignedInAccountsViewControllerForBrowserState: |
| 2109 (ios::ChromeBrowserState*)browserState { |
| 2110 base::scoped_nsobject<UIViewController> accountsViewController( |
| 2111 [[SignedInAccountsViewController alloc] |
| 2112 initWithBrowserState:browserState]); |
| 2113 [[self topPresentedViewController] |
| 2114 presentViewController:accountsViewController |
| 2115 animated:YES |
| 2116 completion:nil]; |
| 2117 } |
| 2118 |
| 2119 - (void)showSettings { |
| 2120 if (_settingsNavigationController) |
| 2121 return; |
| 2122 [[DeferredInitializationRunner sharedInstance] |
| 2123 runBlockIfNecessary:kPrefObserverInit]; |
| 2124 DCHECK(_localStatePrefObserverBridge); |
| 2125 _settingsNavigationController.reset([SettingsNavigationController |
| 2126 newSettingsMainControllerWithMainBrowserState:_mainBrowserState |
| 2127 currentBrowserState:self.currentBrowserState |
| 2128 delegate:self]); |
| 2129 [[self topPresentedViewController] |
| 2130 presentViewController:_settingsNavigationController |
| 2131 animated:YES |
| 2132 completion:nil]; |
| 2133 } |
| 2134 |
| 2135 - (void)showAccountsSettings { |
| 2136 if (_settingsNavigationController) |
| 2137 return; |
| 2138 if ([self currentBrowserState]->IsOffTheRecord()) { |
| 2139 NOTREACHED(); |
| 2140 return; |
| 2141 } |
| 2142 _settingsNavigationController.reset([SettingsNavigationController |
| 2143 newAccountsController:self.currentBrowserState |
| 2144 delegate:self]); |
| 2145 [[self topPresentedViewController] |
| 2146 presentViewController:_settingsNavigationController |
| 2147 animated:YES |
| 2148 completion:nil]; |
| 2149 } |
| 2150 |
| 2151 - (void)showSyncSettings { |
| 2152 if (_settingsNavigationController) |
| 2153 return; |
| 2154 _settingsNavigationController.reset([SettingsNavigationController |
| 2155 newSyncController:_mainBrowserState |
| 2156 allowSwitchSyncAccount:YES |
| 2157 delegate:self]); |
| 2158 [[self topPresentedViewController] |
| 2159 presentViewController:_settingsNavigationController |
| 2160 animated:YES |
| 2161 completion:nil]; |
| 2162 } |
| 2163 |
| 2164 - (void)showSavePasswordsSettings { |
| 2165 if (_settingsNavigationController) |
| 2166 return; |
| 2167 _settingsNavigationController.reset([SettingsNavigationController |
| 2168 newSavePasswordsController:_mainBrowserState |
| 2169 delegate:self]); |
| 2170 [[self topPresentedViewController] |
| 2171 presentViewController:_settingsNavigationController |
| 2172 animated:YES |
| 2173 completion:nil]; |
| 2174 } |
| 2175 |
| 2176 - (void)showReportAnIssue { |
| 2177 if (_settingsNavigationController) |
| 2178 return; |
| 2179 _settingsNavigationController.reset([SettingsNavigationController |
| 2180 newUserFeedbackController:_mainBrowserState |
| 2181 delegate:self |
| 2182 feedbackDataSource:self]); |
| 2183 [[self topPresentedViewController] |
| 2184 presentViewController:_settingsNavigationController |
| 2185 animated:YES |
| 2186 completion:nil]; |
| 2187 } |
| 2188 |
| 2189 - (void)showSyncEncryptionPassphrase { |
| 2190 if (_settingsNavigationController) |
| 2191 return; |
| 2192 _settingsNavigationController.reset([SettingsNavigationController |
| 2193 newSyncEncryptionPassphraseController:_mainBrowserState |
| 2194 delegate:self]); |
| 2195 [[self topPresentedViewController] |
| 2196 presentViewController:_settingsNavigationController |
| 2197 animated:YES |
| 2198 completion:nil]; |
| 2199 } |
| 2200 |
| 2201 - (void)showClearBrowsingDataSettingsController { |
| 2202 if (_settingsNavigationController) |
| 2203 return; |
| 2204 _settingsNavigationController.reset([SettingsNavigationController |
| 2205 newClearBrowsingDataController:_mainBrowserState |
| 2206 delegate:self]); |
| 2207 [[self topPresentedViewController] |
| 2208 presentViewController:_settingsNavigationController |
| 2209 animated:YES |
| 2210 completion:nil]; |
| 2211 } |
| 2212 |
| 2213 - (void)showContextualSearchSettingsController { |
| 2214 if (_settingsNavigationController) |
| 2215 return; |
| 2216 _settingsNavigationController.reset([SettingsNavigationController |
| 2217 newContextualSearchController:_mainBrowserState |
| 2218 delegate:self]); |
| 2219 [[self topPresentedViewController] |
| 2220 presentViewController:_settingsNavigationController |
| 2221 animated:YES |
| 2222 completion:nil]; |
| 2223 } |
| 2224 |
| 2225 - (void)showSignInWithOperation:(AuthenticationOperation)operation |
| 2226 signInAccessPoint:(signin_metrics::AccessPoint)signInAccessPoint |
| 2227 callback:(ShowSigninCommandCompletionCallback)callback { |
| 2228 DCHECK_NE(AUTHENTICATION_OPERATION_DISMISS, operation); |
| 2229 |
| 2230 if (_signinInteractionController) { |
| 2231 // Avoid showing the sign in screen if there is already a sign-in operation |
| 2232 // in progress. |
| 2233 return; |
| 2234 } |
| 2235 |
| 2236 BOOL areSettingsPresented = _settingsNavigationController != NULL; |
| 2237 _signinInteractionController.reset([[SigninInteractionController alloc] |
| 2238 initWithBrowserState:_mainBrowserState |
| 2239 presentingViewController:[self topPresentedViewController] |
| 2240 isPresentedOnSettings:areSettingsPresented |
| 2241 signInAccessPoint:signInAccessPoint]); |
| 2242 |
| 2243 signin_ui::CompletionCallback completion = ^(BOOL success) { |
| 2244 _signinInteractionController.reset(); |
| 2245 if (callback) |
| 2246 callback(success); |
| 2247 }; |
| 2248 |
| 2249 switch (operation) { |
| 2250 case AUTHENTICATION_OPERATION_DISMISS: |
| 2251 // Special case handled above. |
| 2252 NOTREACHED(); |
| 2253 break; |
| 2254 case AUTHENTICATION_OPERATION_REAUTHENTICATE: |
| 2255 [_signinInteractionController |
| 2256 reAuthenticateWithCompletion:completion |
| 2257 viewController:self.mainViewController]; |
| 2258 break; |
| 2259 case AUTHENTICATION_OPERATION_SIGNIN: |
| 2260 [_signinInteractionController |
| 2261 signInWithCompletion:completion |
| 2262 viewController:self.mainViewController]; |
| 2263 break; |
| 2264 } |
| 2265 } |
| 2266 |
| 2267 - (void)showAddAccount { |
| 2268 if (_signinInteractionController) { |
| 2269 // Avoid showing the sign in screen if there is already a sign-in operation |
| 2270 // in progress. |
| 2271 return; |
| 2272 } |
| 2273 |
| 2274 BOOL areSettingsPresented = _settingsNavigationController != NULL; |
| 2275 _signinInteractionController.reset([[SigninInteractionController alloc] |
| 2276 initWithBrowserState:_mainBrowserState |
| 2277 presentingViewController:[self topPresentedViewController] |
| 2278 isPresentedOnSettings:areSettingsPresented |
| 2279 signInAccessPoint:signin_metrics::AccessPoint:: |
| 2280 ACCESS_POINT_UNKNOWN]); |
| 2281 |
| 2282 [_signinInteractionController |
| 2283 addAccountWithCompletion:^(BOOL success) { |
| 2284 _signinInteractionController.reset(); |
| 2285 } |
| 2286 viewController:self.mainViewController]; |
| 2287 } |
| 2288 |
| 2289 - (void)showHistory { |
| 2290 _historyPanelViewController.reset([[HistoryPanelViewController |
| 2291 controllerToPresentForBrowserState:_mainBrowserState |
| 2292 loader:self.currentBVC] retain]); |
| 2293 [self.currentBVC presentViewController:_historyPanelViewController |
| 2294 animated:YES |
| 2295 completion:nil]; |
| 2296 } |
| 2297 |
| 2298 - (void)dismissSigninInteractionController { |
| 2299 // The sign-in interaction controller is destroyed as a result of calling |
| 2300 // |cancelAndDismiss|. Destroying it here may lead to a missing call of the |
| 2301 // |ShowSigninCommandCompletionCallback| passed when starting a show sign-in |
| 2302 // operation. |
| 2303 [_signinInteractionController cancelAndDismiss]; |
| 2304 } |
| 2305 |
| 2306 - (ShowSigninCommandCompletionCallback)successfulSigninCompletion: |
| 2307 (ProceduralBlock)callback { |
| 2308 return [[^(BOOL successful) { |
| 2309 ios::ChromeBrowserState* browserState = [self currentBrowserState]; |
| 2310 if (browserState->IsOffTheRecord()) { |
| 2311 NOTREACHED() |
| 2312 << "Ignore call to |handleSignInFinished| when in incognito."; |
| 2313 return; |
| 2314 } |
| 2315 DCHECK_EQ(self.mainBVC, self.currentBVC); |
| 2316 SigninManager* signinManager = |
| 2317 ios::SigninManagerFactory::GetForBrowserState(browserState); |
| 2318 if (signinManager->IsAuthenticated()) |
| 2319 callback(); |
| 2320 } copy] autorelease]; |
| 2321 } |
| 2322 |
| 2323 - (void)showNativeAppsSettings { |
| 2324 if (_settingsNavigationController) |
| 2325 return; |
| 2326 _settingsNavigationController.reset([SettingsNavigationController |
| 2327 newNativeAppsController:_mainBrowserState |
| 2328 delegate:self]); |
| 2329 [[self topPresentedViewController] |
| 2330 presentViewController:_settingsNavigationController |
| 2331 animated:YES |
| 2332 completion:nil]; |
| 2333 } |
| 2334 |
| 2335 - (void)closeSettingsAnimated:(BOOL)animated |
| 2336 completion:(ProceduralBlock)completion { |
| 2337 DCHECK(_settingsNavigationController); |
| 2338 [_settingsNavigationController settingsWillBeDismissed]; |
| 2339 UIViewController* presentingViewController = |
| 2340 [_settingsNavigationController presentingViewController]; |
| 2341 DCHECK(presentingViewController); |
| 2342 [presentingViewController dismissViewControllerAnimated:animated |
| 2343 completion:^{ |
| 2344 if (completion) |
| 2345 completion(); |
| 2346 }]; |
| 2347 _settingsNavigationController.reset(); |
| 2348 } |
| 2349 |
| 2350 #pragma mark - TabModelObserver |
| 2351 |
| 2352 // Called when the number of tabs changes. Triggers the switcher view when |
| 2353 // the last tab is closed on a device that uses the switcher. |
| 2354 - (void)tabModelDidChangeTabCount:(TabModel*)notifiedTabModel { |
| 2355 TabModel* currentTabModel = [self currentTabModel]; |
| 2356 // Do nothing on initialization. |
| 2357 if (!currentTabModel) |
| 2358 return; |
| 2359 |
| 2360 if (notifiedTabModel.count == 0U) { |
| 2361 if ([notifiedTabModel isOffTheRecord]) { |
| 2362 [self lastIncognitoTabClosed]; |
| 2363 } else { |
| 2364 [self lastRegularTabClosed]; |
| 2365 } |
| 2366 } |
| 2367 } |
| 2368 |
| 2369 #pragma mark - Tab opening utility methods. |
| 2370 |
| 2371 - (Tab*)openOrReuseTabInMode:(ApplicationMode)targetMode |
| 2372 withURL:(const GURL&)url |
| 2373 transition:(ui::PageTransition)transition { |
| 2374 BrowserViewController* targetBVC = |
| 2375 targetMode == ApplicationMode::NORMAL ? self.mainBVC : self.otrBVC; |
| 2376 GURL currentURL; |
| 2377 |
| 2378 Tab* currentTabInTargetBVC = [[targetBVC tabModel] currentTab]; |
| 2379 if (currentTabInTargetBVC) |
| 2380 currentURL = [currentTabInTargetBVC url]; |
| 2381 |
| 2382 if (!(currentTabInTargetBVC && IsURLNtp(currentURL))) { |
| 2383 return [targetBVC addSelectedTabWithURL:url |
| 2384 atIndex:NSNotFound |
| 2385 transition:transition]; |
| 2386 } |
| 2387 |
| 2388 Tab* newTab = currentTabInTargetBVC; |
| 2389 // Don't call loadWithParams for chrome://newtab, it's already loaded. |
| 2390 if (!(IsURLNtp(url))) { |
| 2391 web::NavigationManager::WebLoadParams params(url); |
| 2392 [[newTab webController] loadWithParams:params]; |
| 2393 } |
| 2394 return newTab; |
| 2395 } |
| 2396 |
| 2397 - (Tab*)openSelectedTabInMode:(ApplicationMode)targetMode |
| 2398 withURL:(const GURL&)url |
| 2399 transition:(ui::PageTransition)transition { |
| 2400 BrowserViewController* targetBVC = |
| 2401 targetMode == ApplicationMode::NORMAL ? self.mainBVC : self.otrBVC; |
| 2402 NSUInteger tabIndex = NSNotFound; |
| 2403 |
| 2404 Tab* tab = nil; |
| 2405 if (_tabSwitcherIsActive) { |
| 2406 // If the stack view is already being dismissed, simply add the tab and |
| 2407 // note that when the stack view finishes dismissing, the current BVC should |
| 2408 // be switched to be the main BVC if necessary. |
| 2409 if (_dismissingStackView) { |
| 2410 _modeToDisplayOnStackViewDismissal = |
| 2411 targetMode == ApplicationMode::NORMAL |
| 2412 ? StackViewDismissalMode::NORMAL |
| 2413 : StackViewDismissalMode::INCOGNITO; |
| 2414 tab = [targetBVC addSelectedTabWithURL:url |
| 2415 atIndex:tabIndex |
| 2416 transition:transition]; |
| 2417 } else { |
| 2418 tab = [_tabSwitcherController |
| 2419 dismissWithNewTabAnimationToModel:targetBVC.tabModel |
| 2420 withURL:url |
| 2421 atIndex:tabIndex |
| 2422 transition:transition]; |
| 2423 } |
| 2424 } else { |
| 2425 if (!self.currentBVC.presentedViewController) { |
| 2426 [targetBVC expectNewForegroundTab]; |
| 2427 } |
| 2428 self.currentBVC = targetBVC; |
| 2429 tab = [self openOrReuseTabInMode:targetMode |
| 2430 withURL:url |
| 2431 transition:transition]; |
| 2432 } |
| 2433 |
| 2434 if ([_startupParameters launchVoiceSearch]) { |
| 2435 if (_tabSwitcherIsActive || _dismissingStackView) { |
| 2436 // Since VoiceSearch is presented by the BVC, it must be started after the |
| 2437 // Tab Switcher dismissal completes and the BVC's view is in the |
| 2438 // hiararchy. |
| 2439 _startVoiceSearchAfterTabSwitcherDismissal = YES; |
| 2440 } else { |
| 2441 // When starting the application from the Notification center, |
| 2442 // ApplicationWillResignActive is sent just after startup. |
| 2443 // If the voice search is triggered synchronously, it is immediately |
| 2444 // dismissed. Start it asynchronously instead. |
| 2445 dispatch_async(dispatch_get_main_queue(), ^{ |
| 2446 [self startVoiceSearch]; |
| 2447 }); |
| 2448 } |
| 2449 } else if ([_startupParameters launchQRScanner]) { |
| 2450 if (_tabSwitcherIsActive || _dismissingStackView) { |
| 2451 // QR Scanner is presented by the BVC, similarly to VoiceSearch. It must |
| 2452 // also be started after the BVC's view is in the hierarchy. |
| 2453 [self setStartQRScannerAfterTabSwitcherDismissal:YES]; |
| 2454 } else { |
| 2455 // Start the QR Scanner asynchronously to prevent the application from |
| 2456 // dismissing the modal view if QR Scanner is started from the |
| 2457 // Notification center. |
| 2458 dispatch_async(dispatch_get_main_queue(), ^{ |
| 2459 [self.currentBVC showQRScanner]; |
| 2460 }); |
| 2461 } |
| 2462 } |
| 2463 |
| 2464 if (_restoreHelper) { |
| 2465 // Now that all the operations on the tabs have been done, display the |
| 2466 // restore infobar if needed. |
| 2467 dispatch_async(dispatch_get_main_queue(), ^{ |
| 2468 [_restoreHelper showRestoreIfNeeded:[self currentTabModel]]; |
| 2469 _restoreHelper.reset(); |
| 2470 }); |
| 2471 } |
| 2472 |
| 2473 return tab; |
| 2474 } |
| 2475 |
| 2476 - (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion { |
| 2477 // Immediately hide modals from the provider (alert views, action sheets, |
| 2478 // popovers). They will be ultimately dismissed by their owners, but at least, |
| 2479 // they are not visible. |
| 2480 ios::GetChromeBrowserProvider()->HideModalViewStack(); |
| 2481 |
| 2482 // ChromeIdentityService is responsible for the dialogs displayed by the |
| 2483 // services it wraps. |
| 2484 ios::GetChromeBrowserProvider()->GetChromeIdentityService()->DismissDialogs(); |
| 2485 |
| 2486 // Cancel interaction with SSO. |
| 2487 // First, cancel the signin interaction. |
| 2488 [_signinInteractionController cancel]; |
| 2489 |
| 2490 // Then, depending on what the SSO view controller is presented on, dismiss |
| 2491 // it. |
| 2492 ProceduralBlock completionWithBVC = ^{ |
| 2493 // This will dismiss the SSO view controller. |
| 2494 [self.currentBVC clearPresentedStateWithCompletion:completion]; |
| 2495 }; |
| 2496 ProceduralBlock completionWithoutBVC = ^{ |
| 2497 // This will dismiss the SSO view controller. |
| 2498 [self dismissSigninInteractionController]; |
| 2499 if (completion) |
| 2500 completion(); |
| 2501 }; |
| 2502 |
| 2503 // As a top level rule, if the settings are showing, they need to be |
| 2504 // dismissed. Then, based on whether the BVC is present or not, a different |
| 2505 // completion callback is called. |
| 2506 if (self.currentBVC && _settingsNavigationController) { |
| 2507 // In this case, the settings are up and the BVC is showing. Close the |
| 2508 // settings then call the BVC completion. |
| 2509 [self closeSettingsAnimated:NO completion:completionWithBVC]; |
| 2510 } else if (_settingsNavigationController) { |
| 2511 // In this case, the settings are up but the BVC is not showing. Close the |
| 2512 // settings then call the no-BVC completion. |
| 2513 [self closeSettingsAnimated:NO completion:completionWithoutBVC]; |
| 2514 } else if (self.currentBVC) { |
| 2515 // In this case, the settings are not shown but the BVC is showing. Call the |
| 2516 // BVC completion. |
| 2517 completionWithBVC(); |
| 2518 } else { |
| 2519 // In this case, neither the settings nor the BVC are shown. Call the no-BVC |
| 2520 // completion. |
| 2521 completionWithoutBVC(); |
| 2522 } |
| 2523 |
| 2524 // Verify that no modal views are left presented. |
| 2525 ios::GetChromeBrowserProvider()->LogIfModalViewsArePresented(); |
| 2526 } |
| 2527 |
| 2528 // iOS does not guarantee the order in which the observers are notified by |
| 2529 // the notification center. There are different parts of the application that |
| 2530 // register for UIApplication notifications so recording them in order to |
| 2531 // measure the performance of the app being moved to the foreground / background |
| 2532 // is not reliable. Instead we prefer using designated notifications that are |
| 2533 // posted to the observers on the first available run loop cycle, which |
| 2534 // guarantees that they are delivered to the observer only after UIApplication |
| 2535 // notifications have been treated. |
| 2536 - (void)postNotificationOnNextRunLoopCycle:(NSString*)notificationName { |
| 2537 dispatch_async(dispatch_get_main_queue(), ^{ |
| 2538 [[NSNotificationCenter defaultCenter] postNotificationName:notificationName |
| 2539 object:self]; |
| 2540 }); |
| 2541 } |
| 2542 |
| 2543 - (bool)mustShowRestoreInfobar { |
| 2544 if ([self isFirstLaunchAfterUpgrade]) |
| 2545 return false; |
| 2546 return !GetApplicationContext()->WasLastShutdownClean(); |
| 2547 } |
| 2548 |
| 2549 - (NSMutableSet*)liveSessionsForTabModel:(TabModel*)tabModel { |
| 2550 NSMutableSet* result = [NSMutableSet setWithCapacity:[tabModel count]]; |
| 2551 for (size_t i = 0; i < [tabModel count]; ++i) |
| 2552 [result addObject:[[tabModel tabAtIndex:i] currentSessionID]]; |
| 2553 return result; |
| 2554 } |
| 2555 |
| 2556 - (void)purgeSnapshots { |
| 2557 NSMutableSet* liveSessions = [self liveSessionsForTabModel:self.mainTabModel]; |
| 2558 [liveSessions unionSet:[self liveSessionsForTabModel:self.otrTabModel]]; |
| 2559 // Keep snapshots that are less than one minute old, to prevent a concurrency |
| 2560 // issue if they are created while the purge is running. |
| 2561 [[SnapshotCache sharedInstance] |
| 2562 purgeCacheOlderThan:(base::Time::Now() - base::TimeDelta::FromMinutes(1)) |
| 2563 keeping:liveSessions]; |
| 2564 } |
| 2565 |
| 2566 - (void)markEulaAsAccepted { |
| 2567 PrefService* prefs = GetApplicationContext()->GetLocalState(); |
| 2568 if (!prefs->GetBoolean(prefs::kEulaAccepted)) |
| 2569 prefs->SetBoolean(prefs::kEulaAccepted, true); |
| 2570 prefs->CommitPendingWrite(); |
| 2571 } |
| 2572 |
| 2573 #pragma mark - TabOpening implementation. |
| 2574 |
| 2575 - (void)dismissModalsAndOpenSelectedTabInMode:(ApplicationMode)targetMode |
| 2576 withURL:(const GURL&)url |
| 2577 transition:(ui::PageTransition)transition |
| 2578 completion:(ProceduralBlock)handler { |
| 2579 GURL copyOfURL = url; |
| 2580 [self dismissModalDialogsWithCompletion:^{ |
| 2581 [self openSelectedTabInMode:targetMode |
| 2582 withURL:copyOfURL |
| 2583 transition:transition]; |
| 2584 if (handler) |
| 2585 handler(); |
| 2586 }]; |
| 2587 } |
| 2588 |
| 2589 - (void)openTabFromLaunchOptions:(NSDictionary*)launchOptions |
| 2590 startupInformation:(id<StartupInformation>)startupInformation |
| 2591 appState:(AppState*)appState { |
| 2592 if (launchOptions) { |
| 2593 BOOL applicationIsActive = |
| 2594 [[UIApplication sharedApplication] applicationState] == |
| 2595 UIApplicationStateActive; |
| 2596 |
| 2597 [URLOpener handleLaunchOptions:launchOptions |
| 2598 applicationActive:applicationIsActive |
| 2599 tabOpener:self |
| 2600 startupInformation:startupInformation |
| 2601 appState:appState]; |
| 2602 } |
| 2603 } |
| 2604 |
| 2605 #pragma mark - SettingsNavigationControllerDelegate |
| 2606 |
| 2607 - (void)closeSettings { |
| 2608 [self closeSettingsAnimated:YES completion:NULL]; |
| 2609 } |
| 2610 |
| 2611 // Handle a close settings and open URL command. |
| 2612 - (void)closeSettingsAndOpenUrl:(OpenUrlCommand*)command { |
| 2613 [self openUrlFromSettings:command]; |
| 2614 } |
| 2615 |
| 2616 - (void)closeSettingsAndOpenNewIncognitoTab { |
| 2617 [self closeSettingsAnimated:NO |
| 2618 completion:^{ |
| 2619 [self createNewTabInBVC:self.otrBVC sender:nil]; |
| 2620 }]; |
| 2621 } |
| 2622 |
| 2623 #pragma mark - UserFeedbackDataSource |
| 2624 |
| 2625 - (NSString*)currentPageDisplayURL { |
| 2626 if (_tabSwitcherIsActive) |
| 2627 return nil; |
| 2628 web::WebState* webState = [[[self currentTabModel] currentTab] webState]; |
| 2629 if (!webState) |
| 2630 return nil; |
| 2631 // Returns URL of browser tab that is currently showing. |
| 2632 GURL url = webState->GetVisibleURL(); |
| 2633 base::string16 urlText = url_formatter::FormatUrl(url); |
| 2634 return base::SysUTF16ToNSString(urlText); |
| 2635 } |
| 2636 |
| 2637 - (UIImage*)currentPageScreenshot { |
| 2638 UIView* lastView = self.mainViewController.view; |
| 2639 DCHECK(lastView); |
| 2640 CGFloat scale = 0.0; |
| 2641 // For screenshots of the Stack View we need to use a scale of 1.0 to avoid |
| 2642 // spending too much time since the Stack View can have lots of subviews. |
| 2643 if (_tabSwitcherIsActive) |
| 2644 scale = 1.0; |
| 2645 return CaptureView(lastView, scale); |
| 2646 } |
| 2647 |
| 2648 - (BOOL)currentPageIsIncognito { |
| 2649 return [self currentBrowserState]->IsOffTheRecord(); |
| 2650 } |
| 2651 |
| 2652 - (NSString*)currentPageSyncedUserName { |
| 2653 ios::ChromeBrowserState* browserState = [self currentBrowserState]; |
| 2654 if (browserState->IsOffTheRecord()) |
| 2655 return nil; |
| 2656 SigninManager* signin_manager = |
| 2657 ios::SigninManagerFactory::GetForBrowserState(browserState); |
| 2658 std::string username = signin_manager->GetAuthenticatedAccountInfo().email; |
| 2659 return username.empty() ? nil : base::SysUTF8ToNSString(username); |
| 2660 } |
| 2661 |
| 2662 #pragma mark - UI Automation Testing |
| 2663 |
| 2664 - (void)setUpCurrentBVCForTesting { |
| 2665 // Notify that the set up will close all tabs. |
| 2666 if (!IsIPadIdiom()) { |
| 2667 [[NSNotificationCenter defaultCenter] |
| 2668 postNotificationName:kSetupForTestingWillCloseAllTabsNotification |
| 2669 object:self]; |
| 2670 } |
| 2671 |
| 2672 [self.otrTabModel closeAllTabs]; |
| 2673 [self.mainTabModel closeAllTabs]; |
| 2674 } |
| 2675 |
| 2676 @end |
| 2677 |
| 2678 #pragma mark - TestingOnly |
| 2679 |
| 2680 @implementation MainController (TestingOnly) |
| 2681 |
| 2682 - (DeviceSharingManager*)deviceSharingManager { |
| 2683 return [_browserViewWrangler deviceSharingManager]; |
| 2684 } |
| 2685 |
| 2686 - (UIViewController<TabSwitcher>*)tabSwitcherController { |
| 2687 return _tabSwitcherController.get(); |
| 2688 } |
| 2689 |
| 2690 - (void)setTabSwitcherController:(UIViewController<TabSwitcher>*)controller { |
| 2691 _tabSwitcherController.reset([controller retain]); |
| 2692 } |
| 2693 |
| 2694 - (SigninInteractionController*)signinInteractionController { |
| 2695 return _signinInteractionController.get(); |
| 2696 } |
| 2697 |
| 2698 - (UIViewController*)topPresentedViewController { |
| 2699 return top_view_controller::TopPresentedViewControllerFrom( |
| 2700 self.mainViewController); |
| 2701 } |
| 2702 |
| 2703 - (void)setTabSwitcherActive:(BOOL)active { |
| 2704 _tabSwitcherIsActive = active; |
| 2705 } |
| 2706 |
| 2707 - (BOOL)dismissingTabSwitcher { |
| 2708 return _dismissingStackView; |
| 2709 } |
| 2710 |
| 2711 - (void)setStartupParametersWithURL:(const GURL&)launchURL { |
| 2712 NSString* sourceApplication = @"Fake App"; |
| 2713 _startupParameters.reset([[ChromeAppStartupParameters |
| 2714 newChromeAppStartupParametersWithURL:net::NSURLWithGURL(launchURL) |
| 2715 fromSourceApplication:sourceApplication] retain]); |
| 2716 } |
| 2717 |
| 2718 - (void)setUpAsForegrounded { |
| 2719 _isColdStart = NO; |
| 2720 _browserInitializationStage = INITIALIZATION_STAGE_FOREGROUND; |
| 2721 // Create a BrowserViewWrangler with a null browser state. This will trigger |
| 2722 // assertions if the BrowserViewWrangler is asked to create any BVC or |
| 2723 // tabModel objects, but it will accept assignments to them. |
| 2724 _browserViewWrangler.reset([[BrowserViewWrangler alloc] |
| 2725 initWithBrowserState:nullptr |
| 2726 tabModelObserver:self]); |
| 2727 // This is a test utility method that bypasses the ususal setup steps, so |
| 2728 // verify that the main coordinator hasn't been created yet, then start it |
| 2729 // via lazy initialization. |
| 2730 DCHECK(!_mainCoordinator); |
| 2731 [self.mainCoordinator start]; |
| 2732 } |
| 2733 |
| 2734 - (void)setUpForTestingWithCompletionHandler: |
| 2735 (ProceduralBlock)completionHandler { |
| 2736 self.currentBVC = self.mainBVC; |
| 2737 |
| 2738 int removeAllMask = ~0; |
| 2739 scoped_refptr<CallbackCounter> callbackCounter = |
| 2740 new CallbackCounter(base::BindBlock(^{ |
| 2741 [self setUpCurrentBVCForTesting]; |
| 2742 if (completionHandler) { |
| 2743 completionHandler(); |
| 2744 } |
| 2745 })); |
| 2746 id decrementCallbackCounterCount = ^{ |
| 2747 callbackCounter->DecrementCount(); |
| 2748 }; |
| 2749 |
| 2750 callbackCounter->IncrementCount(); |
| 2751 [self removeBrowsingDataFromBrowserState:_mainBrowserState |
| 2752 mask:removeAllMask |
| 2753 timePeriod:browsing_data::ALL_TIME |
| 2754 completionHandler:decrementCallbackCounterCount]; |
| 2755 } |
| 2756 |
| 2757 @end |
OLD | NEW |