Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: ios/chrome/app/main_controller.mm

Issue 2580363002: Upstream Chrome on iOS source code [1/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ios/chrome/app/main_controller.h ('k') | ios/chrome/app/main_controller_private.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « ios/chrome/app/main_controller.h ('k') | ios/chrome/app/main_controller_private.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698