sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 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/browser/tabs/tab_model.h" |
| 6 | |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 7 | #include <cstdint> |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 8 | #include <utility> |
| 9 | #include <vector> |
| 10 | |
| 11 | #include "base/bind.h" |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 12 | #include "base/callback_helpers.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 13 | #include "base/logging.h" |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 14 | #import "base/mac/foundation_util.h" |
asvitkine | f1899e3 | 2017-01-27 16:30:29 | [diff] [blame] | 15 | #include "base/metrics/histogram_macros.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 16 | #include "base/metrics/user_metrics_action.h" |
kokihoon | 189a402 | 2018-10-13 02:43:44 | [diff] [blame] | 17 | #include "base/stl_util.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 18 | #include "base/strings/sys_string_conversions.h" |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 19 | #include "base/task/cancelable_task_tracker.h" |
Eric Seckler | 217006b | 2018-09-11 15:21:02 | [diff] [blame] | 20 | #include "base/task/post_task.h" |
Sylvain Defresne | 7178d4c | 2017-09-14 13:22:37 | [diff] [blame] | 21 | #include "components/favicon/ios/web_favicon_driver.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 22 | #include "components/navigation_metrics/navigation_metrics.h" |
Ramin Halavati | 251bf1d | 2019-06-19 11:37:15 | [diff] [blame] | 23 | #include "components/profile_metrics/browser_profile_type.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 24 | #include "components/sessions/core/serialized_navigation_entry.h" |
| 25 | #include "components/sessions/core/session_id.h" |
| 26 | #include "components/sessions/core/tab_restore_service.h" |
| 27 | #include "components/sessions/ios/ios_live_tab.h" |
| 28 | #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
Ramin Halavati | 4cef55c | 2019-07-11 12:29:07 | [diff] [blame] | 29 | #include "ios/chrome/browser/browser_state_metrics/browser_state_metrics.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 30 | #include "ios/chrome/browser/chrome_url_constants.h" |
| 31 | #import "ios/chrome/browser/chrome_url_util.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 32 | #include "ios/chrome/browser/crash_loop_detection_util.h" |
| 33 | #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 34 | #import "ios/chrome/browser/metrics/tab_usage_recorder.h" |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 35 | #import "ios/chrome/browser/prerender/prerender_service_factory.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 36 | #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 37 | #import "ios/chrome/browser/sessions/session_ios.h" |
sdefresne | da974c8 | 2017-04-06 17:24:39 | [diff] [blame] | 38 | #import "ios/chrome/browser/sessions/session_service_ios.h" |
sdefresne | 5652815 | 2017-03-22 17:03:28 | [diff] [blame] | 39 | #import "ios/chrome/browser/sessions/session_window_ios.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 40 | #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 41 | #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h" |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 42 | #import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h" |
sdefresne | 8cfdf8f | 2017-01-11 18:22:14 | [diff] [blame] | 43 | #import "ios/chrome/browser/tabs/tab_model_list.h" |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 44 | #import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 45 | #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 46 | #import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.h" |
sdefresne | 39bfaab | 2017-02-20 11:06:36 | [diff] [blame] | 47 | #import "ios/chrome/browser/tabs/tab_parenting_observer.h" |
Eugene But | b56ecad | 2017-08-10 21:09:52 | [diff] [blame] | 48 | #import "ios/chrome/browser/web/page_placeholder_tab_helper.h" |
mrefaat | 2e8bafb | 2019-02-13 03:17:57 | [diff] [blame] | 49 | #import "ios/chrome/browser/web/tab_id_tab_helper.h" |
sdefresne | 62a00bb | 2017-04-10 15:36:05 | [diff] [blame] | 50 | #import "ios/chrome/browser/web_state_list/web_state_list.h" |
sdefresne | 62a00bb | 2017-04-10 15:36:05 | [diff] [blame] | 51 | #import "ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h" |
| 52 | #import "ios/chrome/browser/web_state_list/web_state_list_observer.h" |
| 53 | #import "ios/chrome/browser/web_state_list/web_state_list_serialization.h" |
| 54 | #import "ios/chrome/browser/web_state_list/web_state_opener.h" |
Kurt Horimoto | 070e40b | 2018-08-31 20:50:00 | [diff] [blame] | 55 | #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler.h" |
| 56 | #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler_factory.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 57 | #include "ios/web/public/browser_state.h" |
Eugene But | de5ddcdc | 2019-07-23 00:23:42 | [diff] [blame] | 58 | #import "ios/web/public/navigation/navigation_context.h" |
| 59 | #include "ios/web/public/navigation/navigation_item.h" |
| 60 | #import "ios/web/public/navigation/navigation_manager.h" |
Yi Su | 13f6662 | 2019-05-22 09:20:28 | [diff] [blame] | 61 | #include "ios/web/public/security/certificate_policy_cache.h" |
Yi Su | d5f8216 | 2019-05-28 13:14:48 | [diff] [blame] | 62 | #import "ios/web/public/session/serializable_user_data_manager.h" |
| 63 | #include "ios/web/public/session/session_certificate_policy_cache.h" |
Yi Su | a98c08fb | 2019-06-13 09:03:56 | [diff] [blame] | 64 | #include "ios/web/public/thread/web_task_traits.h" |
| 65 | #include "ios/web/public/thread/web_thread.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 66 | #import "ios/web/public/web_state/web_state_observer_bridge.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 67 | #include "url/gurl.h" |
| 68 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 69 | #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 70 | #error "This file requires ARC support." |
| 71 | #endif |
| 72 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 73 | namespace { |
| 74 | |
| 75 | // Updates CRWSessionCertificatePolicyManager's certificate policy cache. |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 76 | void UpdateCertificatePolicyCacheFromWebState( |
| 77 | const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
kkhorimoto | d3a3986f | 2017-04-05 02:21:41 | [diff] [blame] | 78 | const web::WebState* web_state) { |
sdefresne | 4c8bf63 | 2017-01-26 15:46:47 | [diff] [blame] | 79 | DCHECK(web_state); |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 80 | DCHECK_CURRENTLY_ON(web::WebThread::UI); |
kkhorimoto | d3a3986f | 2017-04-05 02:21:41 | [diff] [blame] | 81 | web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache( |
| 82 | policy_cache); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 83 | } |
| 84 | |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 85 | // Populates the certificate policy cache based on the WebStates of |
| 86 | // |web_state_list|. |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 87 | void RestoreCertificatePolicyCacheFromModel( |
| 88 | const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 89 | WebStateList* web_state_list) { |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 90 | DCHECK_CURRENTLY_ON(web::WebThread::UI); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 91 | for (int index = 0; index < web_state_list->count(); ++index) { |
| 92 | UpdateCertificatePolicyCacheFromWebState( |
| 93 | policy_cache, web_state_list->GetWebStateAt(index)); |
| 94 | } |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 95 | } |
| 96 | |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 97 | // Scrubs the certificate policy cache of all certificates policies except |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 98 | // those for the current entries in |web_state_list|. |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 99 | void CleanCertificatePolicyCache( |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 100 | base::CancelableTaskTracker* task_tracker, |
| 101 | const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 102 | const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 103 | WebStateList* web_state_list) { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 104 | DCHECK(policy_cache); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 105 | DCHECK(web_state_list); |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 106 | DCHECK_CURRENTLY_ON(web::WebThread::UI); |
| 107 | task_tracker->PostTaskAndReply( |
| 108 | task_runner.get(), FROM_HERE, |
| 109 | base::Bind(&web::CertificatePolicyCache::ClearCertificatePolicies, |
| 110 | policy_cache), |
| 111 | base::Bind(&RestoreCertificatePolicyCacheFromModel, policy_cache, |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 112 | base::Unretained(web_state_list))); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 113 | } |
| 114 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 115 | // Returns whether |rhs| and |lhs| are different user agent types. If either |
| 116 | // of them is web::UserAgentType::NONE, then return NO. |
| 117 | BOOL IsTransitionBetweenDesktopAndMobileUserAgent(web::UserAgentType lhs, |
| 118 | web::UserAgentType rhs) { |
| 119 | if (lhs == web::UserAgentType::NONE) |
| 120 | return NO; |
| 121 | |
| 122 | if (rhs == web::UserAgentType::NONE) |
| 123 | return NO; |
| 124 | |
| 125 | return lhs != rhs; |
| 126 | } |
| 127 | |
| 128 | // Returns whether TabUsageRecorder::RecordPageLoadStart should be called for |
| 129 | // the given navigation. |
| 130 | BOOL ShouldRecordPageLoadStartForNavigation( |
| 131 | web::NavigationContext* navigation) { |
| 132 | web::NavigationManager* navigation_manager = |
| 133 | navigation->GetWebState()->GetNavigationManager(); |
| 134 | |
| 135 | web::NavigationItem* last_committed_item = |
| 136 | navigation_manager->GetLastCommittedItem(); |
| 137 | if (!last_committed_item) { |
| 138 | // Opening a child window and loading URL there. |
| 139 | // http://crbug.com/773160 |
| 140 | return NO; |
| 141 | } |
| 142 | |
| 143 | web::NavigationItem* pending_item = navigation_manager->GetPendingItem(); |
| 144 | if (pending_item) { |
| 145 | if (IsTransitionBetweenDesktopAndMobileUserAgent( |
| 146 | pending_item->GetUserAgentType(), |
| 147 | last_committed_item->GetUserAgentType())) { |
| 148 | // Switching between Desktop and Mobile user agent. |
| 149 | return NO; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | ui::PageTransition transition = navigation->GetPageTransition(); |
| 154 | if (!ui::PageTransitionIsNewNavigation(transition)) { |
| 155 | // Back/forward navigation or reload. |
| 156 | return NO; |
| 157 | } |
| 158 | |
| 159 | if ((transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0) { |
| 160 | // Client redirect. |
| 161 | return NO; |
| 162 | } |
| 163 | |
| 164 | static const ui::PageTransition kRecordedPageTransitionTypes[] = { |
| 165 | ui::PAGE_TRANSITION_TYPED, |
| 166 | ui::PAGE_TRANSITION_LINK, |
| 167 | ui::PAGE_TRANSITION_GENERATED, |
| 168 | ui::PAGE_TRANSITION_AUTO_BOOKMARK, |
| 169 | ui::PAGE_TRANSITION_FORM_SUBMIT, |
| 170 | ui::PAGE_TRANSITION_KEYWORD, |
| 171 | ui::PAGE_TRANSITION_KEYWORD_GENERATED, |
| 172 | }; |
| 173 | |
kokihoon | 189a402 | 2018-10-13 02:43:44 | [diff] [blame] | 174 | for (size_t i = 0; i < base::size(kRecordedPageTransitionTypes); ++i) { |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 175 | const ui::PageTransition recorded_type = kRecordedPageTransitionTypes[i]; |
| 176 | if (ui::PageTransitionCoreTypeIs(transition, recorded_type)) { |
| 177 | return YES; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | return NO; |
| 182 | } |
| 183 | |
| 184 | // Records metrics for the interface's orientation. |
| 185 | void RecordInterfaceOrientationMetric() { |
| 186 | switch ([[UIApplication sharedApplication] statusBarOrientation]) { |
| 187 | case UIInterfaceOrientationPortrait: |
| 188 | case UIInterfaceOrientationPortraitUpsideDown: |
| 189 | UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", YES); |
| 190 | break; |
| 191 | case UIInterfaceOrientationLandscapeLeft: |
| 192 | case UIInterfaceOrientationLandscapeRight: |
| 193 | UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", NO); |
| 194 | break; |
| 195 | case UIInterfaceOrientationUnknown: |
| 196 | // TODO(crbug.com/228832): Convert from a boolean histogram to an |
| 197 | // enumerated histogram and log this case as well. |
| 198 | break; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | // Records metrics for main frame navigation. |
| 203 | void RecordMainFrameNavigationMetric(web::WebState* web_state) { |
| 204 | DCHECK(web_state); |
| 205 | DCHECK(web_state->GetBrowserState()); |
| 206 | DCHECK(web_state->GetNavigationManager()); |
| 207 | web::NavigationItem* item = |
| 208 | web_state->GetNavigationManager()->GetLastCommittedItem(); |
| 209 | navigation_metrics::RecordMainFrameNavigation( |
| 210 | item ? item->GetVirtualURL() : GURL::EmptyGURL(), true, |
Ramin Halavati | 4cef55c | 2019-07-11 12:29:07 | [diff] [blame] | 211 | web_state->GetBrowserState()->IsOffTheRecord(), |
| 212 | GetBrowserStateType(web_state->GetBrowserState())); |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 213 | } |
| 214 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 215 | } // anonymous namespace |
| 216 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 217 | @interface TabModel ()<CRWWebStateObserver, WebStateListObserving> { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 218 | // Delegate for the WebStateList. |
| 219 | std::unique_ptr<WebStateListDelegate> _webStateListDelegate; |
| 220 | |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 221 | // Underlying shared model implementation. |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 222 | std::unique_ptr<WebStateList> _webStateList; |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 223 | |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 224 | // WebStateListObservers reacting to modifications of the model (may send |
| 225 | // notification, translate and forward events, update metrics, ...). |
| 226 | std::vector<std::unique_ptr<WebStateListObserver>> _webStateListObservers; |
sdefresne | 39bfaab | 2017-02-20 11:06:36 | [diff] [blame] | 227 | |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 228 | // Strong references to id<WebStateListObserving> wrapped by non-owning |
| 229 | // WebStateListObserverBridges. |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 230 | NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 231 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 232 | // The delegate for sync. |
| 233 | std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 234 | |
| 235 | // Counters for metrics. |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 236 | WebStateListMetricsObserver* _webStateListMetricsObserver; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 237 | |
| 238 | // Backs up property with the same name. |
| 239 | std::unique_ptr<TabUsageRecorder> _tabUsageRecorder; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 240 | // Saves session's state. |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 241 | SessionServiceIOS* _sessionService; |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 242 | |
| 243 | // Used to ensure thread-safety of the certificate policy management code. |
| 244 | base::CancelableTaskTracker _clearPoliciesTaskTracker; |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 245 | |
| 246 | // Used to observe owned Tabs' WebStates. |
| 247 | std::unique_ptr<web::WebStateObserver> _webStateObserver; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | // Session window for the contents of the tab model. |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 251 | @property(nonatomic, readonly) SessionIOS* sessionForSaving; |
Kurt Horimoto | 070e40b | 2018-08-31 20:50:00 | [diff] [blame] | 252 | // Whether the underlying WebStateList's web usage is enabled. |
| 253 | @property(nonatomic, readonly, getter=isWebUsageEnabled) BOOL webUsageEnabled; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 254 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 255 | @end |
| 256 | |
| 257 | @implementation TabModel |
| 258 | |
| 259 | @synthesize browserState = _browserState; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 260 | |
| 261 | #pragma mark - Overriden |
| 262 | |
| 263 | - (void)dealloc { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 264 | // browserStateDestroyed should always have been called before destruction. |
| 265 | DCHECK(!_browserState); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | #pragma mark - Public methods |
| 269 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 270 | - (TabModelSyncedWindowDelegate*)syncedWindowDelegate { |
| 271 | return _syncedWindowDelegate.get(); |
| 272 | } |
| 273 | |
| 274 | - (TabUsageRecorder*)tabUsageRecorder { |
| 275 | return _tabUsageRecorder.get(); |
| 276 | } |
| 277 | |
| 278 | - (BOOL)isOffTheRecord { |
| 279 | return _browserState && _browserState->IsOffTheRecord(); |
| 280 | } |
| 281 | |
| 282 | - (BOOL)isEmpty { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 283 | return _webStateList->empty(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 284 | } |
| 285 | |
sdefresne | 297553e5 | 2017-01-25 17:09:37 | [diff] [blame] | 286 | - (NSUInteger)count { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 287 | DCHECK_GE(_webStateList->count(), 0); |
| 288 | return static_cast<NSUInteger>(_webStateList->count()); |
sdefresne | 297553e5 | 2017-01-25 17:09:37 | [diff] [blame] | 289 | } |
| 290 | |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 291 | - (WebStateList*)webStateList { |
Mohammad Refaat | 71cdcc8 | 2019-07-08 18:25:54 | [diff] [blame] | 292 | DCHECK(_webStateList); |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 293 | return _webStateList.get(); |
| 294 | } |
| 295 | |
Rohit Rao | 0d9f6c3d | 2019-01-22 11:29:35 | [diff] [blame] | 296 | - (instancetype)initWithSessionService:(SessionServiceIOS*)service |
| 297 | browserState:(ios::ChromeBrowserState*)browserState { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 298 | if ((self = [super init])) { |
mrefaat | 294074dc | 2018-11-27 23:25:20 | [diff] [blame] | 299 | _webStateListDelegate = std::make_unique<TabModelWebStateListDelegate>(); |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 300 | _webStateList = std::make_unique<WebStateList>(_webStateListDelegate.get()); |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 301 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 302 | _browserState = browserState; |
| 303 | DCHECK(_browserState); |
| 304 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 305 | _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self); |
| 306 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 307 | // Normal browser states are the only ones to get tab restore. Tab sync |
| 308 | // handles incognito browser states by filtering on profile, so it's |
| 309 | // important to the backend code to always have a sync window delegate. |
| 310 | if (!_browserState->IsOffTheRecord()) { |
| 311 | // Set up the usage recorder before tabs are created. |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 312 | _tabUsageRecorder = std::make_unique<TabUsageRecorder>( |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 313 | _webStateList.get(), |
| 314 | PrerenderServiceFactory::GetForBrowserState(browserState)); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 315 | } |
Sylvain Defresne | 72c1e99 | 2018-01-15 14:26:17 | [diff] [blame] | 316 | _syncedWindowDelegate = |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 317 | std::make_unique<TabModelSyncedWindowDelegate>(_webStateList.get()); |
sdefresne | 4197189 | 2017-02-23 17:24:14 | [diff] [blame] | 318 | |
| 319 | // There must be a valid session service defined to consume session windows. |
| 320 | DCHECK(service); |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 321 | _sessionService = service; |
sdefresne | 4197189 | 2017-02-23 17:24:14 | [diff] [blame] | 322 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 323 | NSMutableArray<id<WebStateListObserving>>* retainedWebStateListObservers = |
| 324 | [[NSMutableArray alloc] init]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 325 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 326 | TabModelClosingWebStateObserver* tabModelClosingWebStateObserver = [ |
| 327 | [TabModelClosingWebStateObserver alloc] |
| 328 | initWithTabModel:self |
| 329 | restoreService:IOSChromeTabRestoreServiceFactory::GetForBrowserState( |
| 330 | _browserState)]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 331 | [retainedWebStateListObservers addObject:tabModelClosingWebStateObserver]; |
| 332 | |
| 333 | _webStateListObservers.push_back( |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 334 | std::make_unique<WebStateListObserverBridge>(self)); |
| 335 | |
| 336 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 337 | std::make_unique<WebStateListObserverBridge>( |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 338 | tabModelClosingWebStateObserver)); |
| 339 | |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 340 | _webStateListObservers.push_back(std::make_unique<TabParentingObserver>()); |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 341 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 342 | TabModelSelectedTabObserver* tabModelSelectedTabObserver = |
| 343 | [[TabModelSelectedTabObserver alloc] initWithTabModel:self]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 344 | [retainedWebStateListObservers addObject:tabModelSelectedTabObserver]; |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 345 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 346 | std::make_unique<WebStateListObserverBridge>( |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 347 | tabModelSelectedTabObserver)); |
| 348 | |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 349 | auto webStateListMetricsObserver = |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 350 | std::make_unique<WebStateListMetricsObserver>(); |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 351 | _webStateListMetricsObserver = webStateListMetricsObserver.get(); |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 352 | _webStateListObservers.push_back(std::move(webStateListMetricsObserver)); |
sdefresne | 4197189 | 2017-02-23 17:24:14 | [diff] [blame] | 353 | |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 354 | for (const auto& webStateListObserver : _webStateListObservers) |
| 355 | _webStateList->AddObserver(webStateListObserver.get()); |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 356 | _retainedWebStateListObservers = [retainedWebStateListObservers copy]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 357 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 358 | // Register for resign active notification. |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 359 | [[NSNotificationCenter defaultCenter] |
| 360 | addObserver:self |
| 361 | selector:@selector(willResignActive:) |
| 362 | name:UIApplicationWillResignActiveNotification |
| 363 | object:nil]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 364 | // Register for background notification. |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 365 | [[NSNotificationCenter defaultCenter] |
| 366 | addObserver:self |
| 367 | selector:@selector(applicationDidEnterBackground:) |
| 368 | name:UIApplicationDidEnterBackgroundNotification |
| 369 | object:nil]; |
sdefresne | 8cfdf8f | 2017-01-11 18:22:14 | [diff] [blame] | 370 | |
| 371 | // Associate with ios::ChromeBrowserState. |
Mohamad Ahmadi | e04d095 | 2017-11-17 23:31:24 | [diff] [blame] | 372 | TabModelList::RegisterTabModelWithChromeBrowserState(_browserState, self); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 373 | } |
| 374 | return self; |
| 375 | } |
| 376 | |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 377 | - (web::WebState*)insertWebStateWithURL:(const GURL&)URL |
| 378 | referrer:(const web::Referrer&)referrer |
| 379 | transition:(ui::PageTransition)transition |
| 380 | opener:(web::WebState*)parentWebState |
| 381 | openedByDOM:(BOOL)openedByDOM |
| 382 | atIndex:(NSUInteger)index |
| 383 | inBackground:(BOOL)inBackground { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 384 | web::NavigationManager::WebLoadParams params(URL); |
| 385 | params.referrer = referrer; |
| 386 | params.transition_type = transition; |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 387 | return [self insertWebStateWithLoadParams:params |
| 388 | opener:parentWebState |
| 389 | openedByDOM:openedByDOM |
| 390 | atIndex:index |
| 391 | inBackground:inBackground]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 392 | } |
| 393 | |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 394 | - (web::WebState*)insertOpenByDOMWebStateWithOpener: |
| 395 | (web::WebState*)openerWebState { |
Eugene But | fcdd811 | 2018-02-06 02:28:04 | [diff] [blame] | 396 | DCHECK(_browserState); |
| 397 | web::WebState::CreateParams createParams(_browserState); |
| 398 | createParams.created_with_opener = YES; |
| 399 | std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams); |
| 400 | |
| 401 | int insertionFlags = |
| 402 | WebStateList::INSERT_FORCE_INDEX | WebStateList::INSERT_ACTIVATE; |
| 403 | int insertedIndex = _webStateList->InsertWebState( |
| 404 | _webStateList->count(), std::move(webState), insertionFlags, |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 405 | WebStateOpener(openerWebState)); |
Eugene But | fcdd811 | 2018-02-06 02:28:04 | [diff] [blame] | 406 | |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 407 | return _webStateList->GetWebStateAt(insertedIndex); |
Eugene But | fcdd811 | 2018-02-06 02:28:04 | [diff] [blame] | 408 | } |
| 409 | |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 410 | - (web::WebState*)insertWebStateWithLoadParams: |
| 411 | (const web::NavigationManager::WebLoadParams&)loadParams |
| 412 | opener:(web::WebState*)parentWebState |
| 413 | openedByDOM:(BOOL)openedByDOM |
| 414 | atIndex:(NSUInteger)index |
| 415 | inBackground:(BOOL)inBackground { |
sdefresne | a639591 | 2017-03-01 01:14:35 | [diff] [blame] | 416 | DCHECK(_browserState); |
Mark Cogan | fb485c33 | 2018-11-13 10:37:07 | [diff] [blame] | 417 | DCHECK(index == TabModelConstants::kTabPositionAutomatically || |
| 418 | index <= self.count); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 419 | |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 420 | int insertionIndex = WebStateList::kInvalidIndex; |
| 421 | int insertionFlags = WebStateList::INSERT_NO_FLAGS; |
| 422 | if (index != TabModelConstants::kTabPositionAutomatically) { |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 423 | DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 424 | insertionIndex = static_cast<int>(index); |
| 425 | insertionFlags |= WebStateList::INSERT_FORCE_INDEX; |
| 426 | } else if (!ui::PageTransitionCoreTypeIs(loadParams.transition_type, |
| 427 | ui::PAGE_TRANSITION_LINK)) { |
| 428 | insertionIndex = _webStateList->count(); |
| 429 | insertionFlags |= WebStateList::INSERT_FORCE_INDEX; |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 430 | } |
| 431 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 432 | if (!inBackground) { |
| 433 | insertionFlags |= WebStateList::INSERT_ACTIVATE; |
| 434 | } |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 435 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 436 | web::WebState::CreateParams createParams(self.browserState); |
| 437 | createParams.created_with_opener = openedByDOM; |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 438 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 439 | std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams); |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 440 | webState->GetNavigationManager()->LoadURLWithParams(loadParams); |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 441 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 442 | insertionIndex = _webStateList->InsertWebState( |
| 443 | insertionIndex, std::move(webState), insertionFlags, |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 444 | WebStateOpener(parentWebState)); |
sdefresne | 7d699dd | 2017-04-05 13:05:23 | [diff] [blame] | 445 | |
Mohammad Refaat | 78374ac | 2019-06-28 17:37:46 | [diff] [blame] | 446 | return _webStateList->GetWebStateAt(insertionIndex); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 447 | } |
| 448 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 449 | - (void)closeTabAtIndex:(NSUInteger)index { |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 450 | DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
Sylvain Defresne | 6b776d7 | 2017-10-18 21:14:06 | [diff] [blame] | 451 | _webStateList->CloseWebStateAt(static_cast<int>(index), |
| 452 | WebStateList::CLOSE_USER_ACTION); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 453 | } |
| 454 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 455 | - (void)closeAllTabs { |
Sylvain Defresne | 6b776d7 | 2017-10-18 21:14:06 | [diff] [blame] | 456 | _webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 457 | } |
| 458 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 459 | - (void)resetSessionMetrics { |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 460 | if (_webStateListMetricsObserver) |
| 461 | _webStateListMetricsObserver->ResetSessionMetrics(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 462 | } |
| 463 | |
| 464 | - (void)recordSessionMetrics { |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 465 | if (_webStateListMetricsObserver) |
| 466 | _webStateListMetricsObserver->RecordSessionMetrics(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 467 | } |
| 468 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 469 | - (void)setPrimary:(BOOL)primary { |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 470 | if (_tabUsageRecorder) { |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 471 | _tabUsageRecorder->RecordPrimaryTabModelChange( |
| 472 | primary, self.webStateList->GetActiveWebState()); |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 473 | } |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 474 | } |
| 475 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 476 | // NOTE: This can be called multiple times, so must be robust against that. |
| 477 | - (void)browserStateDestroyed { |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 478 | if (!_browserState) |
| 479 | return; |
| 480 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 481 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
Mohamad Ahmadi | e04d095 | 2017-11-17 23:31:24 | [diff] [blame] | 482 | TabModelList::UnregisterTabModelFromChromeBrowserState(_browserState, self); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 483 | _browserState = nullptr; |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 484 | |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 485 | // Clear weak pointer to observers before destroying them. |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 486 | _webStateListMetricsObserver = nullptr; |
| 487 | |
| 488 | // Close all tabs. Do this in an @autoreleasepool as WebStateList observers |
| 489 | // will be notified (they are unregistered later). As some of them may be |
| 490 | // implemented in Objective-C and unregister themselves in their -dealloc |
| 491 | // method, ensure they -autorelease introduced by ARC are processed before |
| 492 | // the WebStateList destructor is called. |
| 493 | @autoreleasepool { |
Sylvain Defresne | 6b776d7 | 2017-10-18 21:14:06 | [diff] [blame] | 494 | _webStateList->CloseAllWebStates(WebStateList::CLOSE_NO_FLAGS); |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 495 | } |
| 496 | |
| 497 | // Unregister all observers after closing all the tabs as some of them are |
| 498 | // required to properly clean up the Tabs. |
| 499 | for (const auto& webStateListObserver : _webStateListObservers) |
| 500 | _webStateList->RemoveObserver(webStateListObserver.get()); |
| 501 | _webStateListObservers.clear(); |
| 502 | _retainedWebStateListObservers = nil; |
| 503 | |
| 504 | _clearPoliciesTaskTracker.TryCancelAll(); |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 505 | _tabUsageRecorder.reset(); |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 506 | _webStateObserver.reset(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 507 | } |
| 508 | |
Mark Cogan | 30c2449 | 2019-01-16 16:52:58 | [diff] [blame] | 509 | #pragma mark - SessionWindowRestoring(public) |
| 510 | |
Mark Cogan | 30c2449 | 2019-01-16 16:52:58 | [diff] [blame] | 511 | - (void)saveSessionImmediately:(BOOL)immediately { |
| 512 | // Do nothing if there are tabs in the model but no selected tab. This is |
| 513 | // a transitional state. |
Mohammad Refaat | 3fb2b23 | 2019-08-07 20:49:53 | [diff] [blame^] | 514 | if ((!_webStateList->GetActiveWebState() && _webStateList->count()) || |
| 515 | !_browserState) |
Mark Cogan | 30c2449 | 2019-01-16 16:52:58 | [diff] [blame] | 516 | return; |
| 517 | NSString* statePath = |
| 518 | base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe()); |
| 519 | __weak TabModel* weakSelf = self; |
| 520 | SessionIOSFactory sessionFactory = ^{ |
| 521 | return weakSelf.sessionForSaving; |
| 522 | }; |
| 523 | [_sessionService saveSession:sessionFactory |
| 524 | directory:statePath |
| 525 | immediately:immediately]; |
| 526 | } |
| 527 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 528 | #pragma mark - Private methods |
| 529 | |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 530 | - (SessionIOS*)sessionForSaving { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 531 | // Build the array of sessions. Copy the session objects as the saving will |
| 532 | // be done on a separate thread. |
| 533 | // TODO(crbug.com/661986): This could get expensive especially since this |
| 534 | // window may never be saved (if another call comes in before the delay). |
sdefresne | 68f546a | 2017-04-20 10:54:03 | [diff] [blame] | 535 | return [[SessionIOS alloc] |
| 536 | initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 537 | } |
| 538 | |
Kurt Horimoto | 070e40b | 2018-08-31 20:50:00 | [diff] [blame] | 539 | - (BOOL)isWebUsageEnabled { |
| 540 | DCHECK(_browserState); |
| 541 | return WebStateListWebUsageEnablerFactory::GetInstance() |
| 542 | ->GetForBrowserState(_browserState) |
| 543 | ->IsWebUsageEnabled(); |
| 544 | } |
| 545 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 546 | - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window |
Rohit Rao | 0d9f6c3d | 2019-01-22 11:29:35 | [diff] [blame] | 547 | forInitialRestore:(BOOL)initialRestore { |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 548 | DCHECK(_browserState); |
Rohit Rao | 0d9f6c3d | 2019-01-22 11:29:35 | [diff] [blame] | 549 | |
| 550 | // It is only ok to pass a nil |window| during the initial restore. |
| 551 | DCHECK(window || initialRestore); |
| 552 | |
Mohammad Refaat | 89ddbf1f | 2019-05-23 12:12:48 | [diff] [blame] | 553 | // Setting the sesion progress to |YES|, so BVC can check it to work around |
| 554 | // crbug.com/763964. |
| 555 | _restoringSession = YES; |
| 556 | base::ScopedClosureRunner updateSessionRestorationProgress(base::BindOnce(^{ |
| 557 | _restoringSession = NO; |
| 558 | })); |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 559 | |
sdefresne | 68f546a | 2017-04-20 10:54:03 | [diff] [blame] | 560 | if (!window.sessions.count) |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 561 | return NO; |
| 562 | |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 563 | int oldCount = _webStateList->count(); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 564 | DCHECK_GE(oldCount, 0); |
| 565 | |
sdefresne | 08c0ab4 | 2017-04-05 12:49:43 | [diff] [blame] | 566 | web::WebState::CreateParams createParams(_browserState); |
| 567 | DeserializeWebStateList( |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 568 | _webStateList.get(), window, |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 569 | base::BindRepeating(&web::WebState::CreateWithStorageSession, |
| 570 | createParams)); |
sdefresne | 08c0ab4 | 2017-04-05 12:49:43 | [diff] [blame] | 571 | |
| 572 | DCHECK_GT(_webStateList->count(), oldCount); |
| 573 | int restoredCount = _webStateList->count() - oldCount; |
sdefresne | 68f546a | 2017-04-20 10:54:03 | [diff] [blame] | 574 | DCHECK_EQ(window.sessions.count, static_cast<NSUInteger>(restoredCount)); |
sdefresne | 08c0ab4 | 2017-04-05 12:49:43 | [diff] [blame] | 575 | |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 576 | scoped_refptr<web::CertificatePolicyCache> policyCache = |
| 577 | web::BrowserState::GetCertificatePolicyCache(_browserState); |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 578 | |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 579 | std::vector<web::WebState*> restoredWebStates; |
| 580 | if (_tabUsageRecorder) |
| 581 | restoredWebStates.reserve(window.sessions.count); |
sdefresne | e0a9ba6 | 2017-02-23 09:14:11 | [diff] [blame] | 582 | |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 583 | for (int index = oldCount; index < _webStateList->count(); ++index) { |
| 584 | web::WebState* webState = _webStateList->GetWebStateAt(index); |
Sylvain Defresne | 7178d4c | 2017-09-14 13:22:37 | [diff] [blame] | 585 | web::NavigationItem* visible_item = |
| 586 | webState->GetNavigationManager()->GetVisibleItem(); |
Gauthier Ambard | 32fe0dd | 2017-09-20 14:25:17 | [diff] [blame] | 587 | |
| 588 | if (!(visible_item && |
Sylvain Defresne | c57fe76 | 2018-01-29 09:50:42 | [diff] [blame] | 589 | visible_item->GetVirtualURL() == kChromeUINewTabURL)) { |
Gauthier Ambard | 32fe0dd | 2017-09-20 14:25:17 | [diff] [blame] | 590 | PagePlaceholderTabHelper::FromWebState(webState) |
| 591 | ->AddPlaceholderForNextNavigation(); |
| 592 | } |
| 593 | |
Sylvain Defresne | 7178d4c | 2017-09-14 13:22:37 | [diff] [blame] | 594 | if (visible_item && visible_item->GetVirtualURL().is_valid()) { |
| 595 | favicon::WebFaviconDriver::FromWebState(webState)->FetchFavicon( |
| 596 | visible_item->GetVirtualURL(), /*is_same_document=*/false); |
| 597 | } |
| 598 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 599 | // Restore the CertificatePolicyCache (note that webState is invalid after |
sdefresne | bf75f02 | 2017-03-07 17:48:35 | [diff] [blame] | 600 | // passing it via move semantic to -initWithWebState:model:). |
Eugene But | b56ecad | 2017-08-10 21:09:52 | [diff] [blame] | 601 | UpdateCertificatePolicyCacheFromWebState(policyCache, webState); |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 602 | |
| 603 | if (_tabUsageRecorder) |
| 604 | restoredWebStates.push_back(webState); |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 605 | } |
sdefresne | e0a9ba6 | 2017-02-23 09:14:11 | [diff] [blame] | 606 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 607 | // If there was only one tab and it was the new tab page, clobber it. |
| 608 | BOOL closedNTPTab = NO; |
| 609 | if (oldCount == 1) { |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 610 | web::WebState* webState = _webStateList->GetWebStateAt(0); |
kkhorimoto | 2a192b8 | 2017-06-01 20:11:08 | [diff] [blame] | 611 | BOOL hasPendingLoad = |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 612 | webState->GetNavigationManager()->GetPendingItem() != nullptr; |
Sylvain Defresne | e7f2c8a | 2017-10-17 02:39:19 | [diff] [blame] | 613 | if (!hasPendingLoad && |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 614 | webState->GetLastCommittedURL() == kChromeUINewTabURL) { |
| 615 | _webStateList->CloseWebStateAt(0, WebStateList::CLOSE_USER_ACTION); |
| 616 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 617 | closedNTPTab = YES; |
| 618 | oldCount = 0; |
| 619 | } |
| 620 | } |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 621 | if (_tabUsageRecorder) { |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 622 | _tabUsageRecorder->InitialRestoredTabs(_webStateList->GetActiveWebState(), |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 623 | restoredWebStates); |
| 624 | } |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 625 | |
Rohit Rao | 0d9f6c3d | 2019-01-22 11:29:35 | [diff] [blame] | 626 | if (initialRestore) { |
| 627 | // Restore the session and reset the session metrics (as the event have |
| 628 | // not been generated by the user but by a cold start cycle). |
| 629 | [self resetSessionMetrics]; |
| 630 | } |
| 631 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 632 | return closedNTPTab; |
| 633 | } |
| 634 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 635 | #pragma mark - Notification Handlers |
| 636 | |
| 637 | // Called when UIApplicationWillResignActiveNotification is received. |
| 638 | - (void)willResignActive:(NSNotification*)notify { |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 639 | if (self.webUsageEnabled && _webStateList->GetActiveWebState()) { |
mrefaat | 2e8bafb | 2019-02-13 03:17:57 | [diff] [blame] | 640 | NSString* tabId = |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 641 | TabIdTabHelper::FromWebState(_webStateList->GetActiveWebState()) |
| 642 | ->tab_id(); |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 643 | [SnapshotCacheFactory::GetForBrowserState(_browserState) |
mrefaat | 2e8bafb | 2019-02-13 03:17:57 | [diff] [blame] | 644 | willBeSavedGreyWhenBackgrounding:tabId]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 645 | } |
| 646 | } |
| 647 | |
| 648 | // Called when UIApplicationDidEnterBackgroundNotification is received. |
| 649 | - (void)applicationDidEnterBackground:(NSNotification*)notify { |
| 650 | if (!_browserState) |
| 651 | return; |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 652 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 653 | // Evict all the certificate policies except for the current entries of the |
| 654 | // active sessions. |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 655 | CleanCertificatePolicyCache( |
| 656 | &_clearPoliciesTaskTracker, |
Eric Seckler | 217006b | 2018-09-11 15:21:02 | [diff] [blame] | 657 | base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}), |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 658 | web::BrowserState::GetCertificatePolicyCache(_browserState), |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 659 | _webStateList.get()); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 660 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 661 | // Normally, the session is saved after some timer expires but since the app |
| 662 | // is about to enter the background send YES to save the session immediately. |
| 663 | [self saveSessionImmediately:YES]; |
| 664 | |
| 665 | // Write out a grey version of the current website to disk. |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 666 | if (self.webUsageEnabled && _webStateList->GetActiveWebState()) { |
mrefaat | 2e8bafb | 2019-02-13 03:17:57 | [diff] [blame] | 667 | NSString* tabId = |
Mohammad Refaat | cca3b06c | 2019-07-16 16:14:03 | [diff] [blame] | 668 | TabIdTabHelper::FromWebState(_webStateList->GetActiveWebState()) |
| 669 | ->tab_id(); |
mrefaat | 2e8bafb | 2019-02-13 03:17:57 | [diff] [blame] | 670 | |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 671 | [SnapshotCacheFactory::GetForBrowserState(_browserState) |
mrefaat | 2e8bafb | 2019-02-13 03:17:57 | [diff] [blame] | 672 | saveGreyInBackgroundForSessionID:tabId]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 673 | } |
| 674 | } |
| 675 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 676 | #pragma mark - CRWWebStateObserver |
| 677 | |
| 678 | - (void)webState:(web::WebState*)webState |
Eugene But | 5c6a82d | 2019-01-10 17:15:32 | [diff] [blame] | 679 | didFinishNavigation:(web::NavigationContext*)navigation { |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 680 | |
Eugene But | 5c6a82d | 2019-01-10 17:15:32 | [diff] [blame] | 681 | if (!navigation->IsSameDocument() && navigation->HasCommitted() && |
| 682 | !self.offTheRecord) { |
Eugene But | b117c32 | 2019-01-07 21:00:28 | [diff] [blame] | 683 | int tabCount = static_cast<int>(self.count); |
| 684 | UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tabCount, 1, 200, 50); |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 685 | } |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 686 | } |
| 687 | |
| 688 | - (void)webState:(web::WebState*)webState |
| 689 | didStartNavigation:(web::NavigationContext*)navigation { |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 690 | |
| 691 | // In order to avoid false positive in the crash loop detection, disable the |
| 692 | // counter as soon as an URL is loaded. This requires an user action and is a |
| 693 | // significant source of crashes. Ignore NTP as it is loaded by default after |
| 694 | // a crash. |
| 695 | if (navigation->GetUrl().host_piece() != kChromeUINewTabHost) { |
| 696 | static dispatch_once_t dispatch_once_token; |
| 697 | dispatch_once(&dispatch_once_token, ^{ |
| 698 | crash_util::ResetFailedStartupAttemptCount(); |
| 699 | }); |
| 700 | } |
| 701 | |
| 702 | if (_tabUsageRecorder && ShouldRecordPageLoadStartForNavigation(navigation)) { |
| 703 | _tabUsageRecorder->RecordPageLoadStart(webState); |
| 704 | } |
| 705 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 706 | DCHECK(webState->GetNavigationManager()); |
| 707 | web::NavigationItem* navigationItem = |
| 708 | webState->GetNavigationManager()->GetPendingItem(); |
| 709 | |
| 710 | // TODO(crbug.com/676129): the pending item is not correctly set when the |
| 711 | // page is reloading, use the last committed item if pending item is null. |
| 712 | // Remove this once tracking bug is fixed. |
| 713 | if (!navigationItem) { |
| 714 | navigationItem = webState->GetNavigationManager()->GetLastCommittedItem(); |
| 715 | } |
| 716 | |
Eugene But | 9c3e90c | 2019-01-04 20:45:04 | [diff] [blame] | 717 | if (!navigationItem) { |
| 718 | // Pending item may not exist due to the bug in //ios/web layer. |
| 719 | // TODO(crbug.com/899827): remove this early return once GetPendingItem() |
| 720 | // always return valid object inside WebStateObserver::DidStartNavigation() |
| 721 | // callback. |
| 722 | // |
| 723 | // Note that GetLastCommittedItem() returns null if navigation manager does |
| 724 | // not have committed items (which is normal situation). |
| 725 | return; |
| 726 | } |
| 727 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 728 | [[OmniboxGeolocationController sharedInstance] |
| 729 | addLocationToNavigationItem:navigationItem |
| 730 | browserState:ios::ChromeBrowserState::FromBrowserState( |
| 731 | webState->GetBrowserState())]; |
| 732 | } |
| 733 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 734 | - (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success { |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 735 | RecordInterfaceOrientationMetric(); |
| 736 | RecordMainFrameNavigationMetric(webState); |
| 737 | |
mrefaat | c977e37 | 2019-02-04 22:19:18 | [diff] [blame] | 738 | [[OmniboxGeolocationController sharedInstance] |
| 739 | finishPageLoadForWebState:webState |
| 740 | loadSuccess:success]; |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 741 | } |
| 742 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 743 | - (void)webStateDestroyed:(web::WebState*)webState { |
| 744 | // The TabModel is removed from WebState's observer when the WebState is |
| 745 | // detached from WebStateList which happens before WebState destructor, |
| 746 | // so this method should never be called. |
| 747 | NOTREACHED(); |
| 748 | } |
| 749 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame] | 750 | #pragma mark - WebStateListObserving |
| 751 | |
| 752 | - (void)webStateList:(WebStateList*)webStateList |
| 753 | didInsertWebState:(web::WebState*)webState |
| 754 | atIndex:(int)index |
| 755 | activating:(BOOL)activating { |
| 756 | DCHECK(webState); |
| 757 | webState->AddObserver(_webStateObserver.get()); |
| 758 | } |
| 759 | |
| 760 | - (void)webStateList:(WebStateList*)webStateList |
| 761 | didReplaceWebState:(web::WebState*)oldWebState |
| 762 | withWebState:(web::WebState*)newWebState |
| 763 | atIndex:(int)atIndex { |
| 764 | DCHECK(oldWebState); |
| 765 | DCHECK(newWebState); |
| 766 | newWebState->AddObserver(_webStateObserver.get()); |
| 767 | oldWebState->RemoveObserver(_webStateObserver.get()); |
| 768 | } |
| 769 | |
| 770 | - (void)webStateList:(WebStateList*)webStateList |
| 771 | didDetachWebState:(web::WebState*)webState |
| 772 | atIndex:(int)atIndex { |
| 773 | DCHECK(webState); |
| 774 | webState->RemoveObserver(_webStateObserver.get()); |
| 775 | } |
| 776 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 777 | @end |