blob: 944c089bbaf4119782dc550c532f01d5811c5224 [file] [log] [blame]
sdefresne67dfdd62016-12-19 12:43:121// 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
sdefresne32c0c382017-02-17 16:37:267#include <cstdint>
sdefresne67dfdd62016-12-19 12:43:128#include <utility>
9#include <vector>
10
11#include "base/bind.h"
Sylvain Defresnee8ae6ad2017-09-14 14:22:1012#include "base/callback_helpers.h"
sdefresne67dfdd62016-12-19 12:43:1213#include "base/logging.h"
sdefresne2c600c52017-04-04 16:49:5914#import "base/mac/foundation_util.h"
asvitkinef1899e32017-01-27 16:30:2915#include "base/metrics/histogram_macros.h"
sdefresne67dfdd62016-12-19 12:43:1216#include "base/metrics/user_metrics_action.h"
kokihoon189a4022018-10-13 02:43:4417#include "base/stl_util.h"
sdefresne67dfdd62016-12-19 12:43:1218#include "base/strings/sys_string_conversions.h"
sdefresnec92bada2017-02-08 14:43:4919#include "base/task/cancelable_task_tracker.h"
Eric Seckler217006b2018-09-11 15:21:0220#include "base/task/post_task.h"
Sylvain Defresne7178d4c2017-09-14 13:22:3721#include "components/favicon/ios/web_favicon_driver.h"
Sylvain Defresne94394332018-01-22 13:11:0022#include "components/navigation_metrics/navigation_metrics.h"
Ramin Halavati251bf1d2019-06-19 11:37:1523#include "components/profile_metrics/browser_profile_type.h"
sdefresne67dfdd62016-12-19 12:43:1224#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 Halavati4cef55c2019-07-11 12:29:0729#include "ios/chrome/browser/browser_state_metrics/browser_state_metrics.h"
sdefresne67dfdd62016-12-19 12:43:1230#include "ios/chrome/browser/chrome_url_constants.h"
31#import "ios/chrome/browser/chrome_url_util.h"
Sylvain Defresne94394332018-01-22 13:11:0032#include "ios/chrome/browser/crash_loop_detection_util.h"
33#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
sdefresne67dfdd62016-12-19 12:43:1234#import "ios/chrome/browser/metrics/tab_usage_recorder.h"
Sylvain Defresne890975f52017-08-24 17:42:2635#import "ios/chrome/browser/prerender/prerender_service_factory.h"
sdefresne67dfdd62016-12-19 12:43:1236#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
sdefresnedcc6c6e2017-04-19 15:49:0237#import "ios/chrome/browser/sessions/session_ios.h"
sdefresneda974c82017-04-06 17:24:3938#import "ios/chrome/browser/sessions/session_service_ios.h"
sdefresne56528152017-03-22 17:03:2839#import "ios/chrome/browser/sessions/session_window_ios.h"
sdefresne67dfdd62016-12-19 12:43:1240#import "ios/chrome/browser/snapshots/snapshot_cache.h"
Sylvain Defresne528c17b2017-06-08 15:00:2141#import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
sdefresne2c600c52017-04-04 16:49:5942#import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h"
sdefresne8cfdf8f2017-01-11 18:22:1443#import "ios/chrome/browser/tabs/tab_model_list.h"
sdefresne2f7781c2017-03-02 19:12:4644#import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h"
sdefresne67dfdd62016-12-19 12:43:1245#import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h"
sdefresne365a73a2017-03-14 15:07:1346#import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.h"
sdefresne39bfaab2017-02-20 11:06:3647#import "ios/chrome/browser/tabs/tab_parenting_observer.h"
Eugene Butb56ecad2017-08-10 21:09:5248#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
mrefaat2e8bafb2019-02-13 03:17:5749#import "ios/chrome/browser/web/tab_id_tab_helper.h"
sdefresne62a00bb2017-04-10 15:36:0550#import "ios/chrome/browser/web_state_list/web_state_list.h"
sdefresne62a00bb2017-04-10 15:36:0551#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 Horimoto070e40b2018-08-31 20:50:0055#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"
sdefresne67dfdd62016-12-19 12:43:1257#include "ios/web/public/browser_state.h"
Eugene Butde5ddcdc2019-07-23 00:23:4258#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 Su13f66622019-05-22 09:20:2861#include "ios/web/public/security/certificate_policy_cache.h"
Yi Sud5f82162019-05-28 13:14:4862#import "ios/web/public/session/serializable_user_data_manager.h"
63#include "ios/web/public/session/session_certificate_policy_cache.h"
Yi Sua98c08fb2019-06-13 09:03:5664#include "ios/web/public/thread/web_task_traits.h"
65#include "ios/web/public/thread/web_thread.h"
Sylvain Defresne94394332018-01-22 13:11:0066#import "ios/web/public/web_state/web_state_observer_bridge.h"
sdefresne67dfdd62016-12-19 12:43:1267#include "url/gurl.h"
68
sdefresne4162a4dc2017-05-15 11:26:2469#if !defined(__has_feature) || !__has_feature(objc_arc)
70#error "This file requires ARC support."
71#endif
72
sdefresne67dfdd62016-12-19 12:43:1273namespace {
74
75// Updates CRWSessionCertificatePolicyManager's certificate policy cache.
sdefresnec92bada2017-02-08 14:43:4976void UpdateCertificatePolicyCacheFromWebState(
77 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
kkhorimotod3a3986f2017-04-05 02:21:4178 const web::WebState* web_state) {
sdefresne4c8bf632017-01-26 15:46:4779 DCHECK(web_state);
sdefresnec92bada2017-02-08 14:43:4980 DCHECK_CURRENTLY_ON(web::WebThread::UI);
kkhorimotod3a3986f2017-04-05 02:21:4181 web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache(
82 policy_cache);
sdefresne67dfdd62016-12-19 12:43:1283}
84
sdefresne32c0c382017-02-17 16:37:2685// Populates the certificate policy cache based on the WebStates of
86// |web_state_list|.
sdefresnec92bada2017-02-08 14:43:4987void RestoreCertificatePolicyCacheFromModel(
88 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:2689 WebStateList* web_state_list) {
sdefresnec92bada2017-02-08 14:43:4990 DCHECK_CURRENTLY_ON(web::WebThread::UI);
sdefresne32c0c382017-02-17 16:37:2691 for (int index = 0; index < web_state_list->count(); ++index) {
92 UpdateCertificatePolicyCacheFromWebState(
93 policy_cache, web_state_list->GetWebStateAt(index));
94 }
sdefresne67dfdd62016-12-19 12:43:1295}
96
sdefresnec92bada2017-02-08 14:43:4997// Scrubs the certificate policy cache of all certificates policies except
sdefresne32c0c382017-02-17 16:37:2698// those for the current entries in |web_state_list|.
sdefresne67dfdd62016-12-19 12:43:1299void CleanCertificatePolicyCache(
sdefresnec92bada2017-02-08 14:43:49100 base::CancelableTaskTracker* task_tracker,
101 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
102 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:26103 WebStateList* web_state_list) {
sdefresne67dfdd62016-12-19 12:43:12104 DCHECK(policy_cache);
sdefresne32c0c382017-02-17 16:37:26105 DCHECK(web_state_list);
sdefresnec92bada2017-02-08 14:43:49106 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,
sdefresne32c0c382017-02-17 16:37:26112 base::Unretained(web_state_list)));
sdefresne67dfdd62016-12-19 12:43:12113}
114
Sylvain Defresne94394332018-01-22 13:11:00115// Returns whether |rhs| and |lhs| are different user agent types. If either
116// of them is web::UserAgentType::NONE, then return NO.
117BOOL 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.
130BOOL 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
kokihoon189a4022018-10-13 02:43:44174 for (size_t i = 0; i < base::size(kRecordedPageTransitionTypes); ++i) {
Sylvain Defresne94394332018-01-22 13:11:00175 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.
185void 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.
203void 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 Halavati4cef55c2019-07-11 12:29:07211 web_state->GetBrowserState()->IsOffTheRecord(),
212 GetBrowserStateType(web_state->GetBrowserState()));
Sylvain Defresne94394332018-01-22 13:11:00213}
214
sdefresne67dfdd62016-12-19 12:43:12215} // anonymous namespace
216
Sylvain Defresne94394332018-01-22 13:11:00217@interface TabModel ()<CRWWebStateObserver, WebStateListObserving> {
sdefresne365a73a2017-03-14 15:07:13218 // Delegate for the WebStateList.
219 std::unique_ptr<WebStateListDelegate> _webStateListDelegate;
220
sdefresne32c0c382017-02-17 16:37:26221 // Underlying shared model implementation.
sdefresne365a73a2017-03-14 15:07:13222 std::unique_ptr<WebStateList> _webStateList;
sdefresne32c0c382017-02-17 16:37:26223
sdefresneee210332017-03-24 10:36:54224 // 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;
sdefresne39bfaab2017-02-20 11:06:36227
sdefresne3b0155e92017-04-12 15:18:53228 // Strong references to id<WebStateListObserving> wrapped by non-owning
229 // WebStateListObserverBridges.
sdefresne4162a4dc2017-05-15 11:26:24230 NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers;
sdefresne3b0155e92017-04-12 15:18:53231
sdefresne67dfdd62016-12-19 12:43:12232 // The delegate for sync.
233 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate;
sdefresne67dfdd62016-12-19 12:43:12234
235 // Counters for metrics.
sdefresne2f7781c2017-03-02 19:12:46236 WebStateListMetricsObserver* _webStateListMetricsObserver;
sdefresne67dfdd62016-12-19 12:43:12237
238 // Backs up property with the same name.
239 std::unique_ptr<TabUsageRecorder> _tabUsageRecorder;
sdefresne67dfdd62016-12-19 12:43:12240 // Saves session's state.
sdefresne4162a4dc2017-05-15 11:26:24241 SessionServiceIOS* _sessionService;
sdefresnec92bada2017-02-08 14:43:49242
243 // Used to ensure thread-safety of the certificate policy management code.
244 base::CancelableTaskTracker _clearPoliciesTaskTracker;
Sylvain Defresne94394332018-01-22 13:11:00245
246 // Used to observe owned Tabs' WebStates.
247 std::unique_ptr<web::WebStateObserver> _webStateObserver;
sdefresne67dfdd62016-12-19 12:43:12248}
249
250// Session window for the contents of the tab model.
sdefresnedcc6c6e2017-04-19 15:49:02251@property(nonatomic, readonly) SessionIOS* sessionForSaving;
Kurt Horimoto070e40b2018-08-31 20:50:00252// Whether the underlying WebStateList's web usage is enabled.
253@property(nonatomic, readonly, getter=isWebUsageEnabled) BOOL webUsageEnabled;
sdefresne67dfdd62016-12-19 12:43:12254
sdefresne67dfdd62016-12-19 12:43:12255@end
256
257@implementation TabModel
258
259@synthesize browserState = _browserState;
sdefresne67dfdd62016-12-19 12:43:12260
261#pragma mark - Overriden
262
263- (void)dealloc {
sdefresne67dfdd62016-12-19 12:43:12264 // browserStateDestroyed should always have been called before destruction.
265 DCHECK(!_browserState);
sdefresne67dfdd62016-12-19 12:43:12266}
267
268#pragma mark - Public methods
269
sdefresne67dfdd62016-12-19 12:43:12270- (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 {
sdefresne365a73a2017-03-14 15:07:13283 return _webStateList->empty();
sdefresne67dfdd62016-12-19 12:43:12284}
285
sdefresne297553e52017-01-25 17:09:37286- (NSUInteger)count {
sdefresne365a73a2017-03-14 15:07:13287 DCHECK_GE(_webStateList->count(), 0);
288 return static_cast<NSUInteger>(_webStateList->count());
sdefresne297553e52017-01-25 17:09:37289}
290
sdefresneee210332017-03-24 10:36:54291- (WebStateList*)webStateList {
Mohammad Refaat71cdcc82019-07-08 18:25:54292 DCHECK(_webStateList);
sdefresneee210332017-03-24 10:36:54293 return _webStateList.get();
294}
295
Rohit Rao0d9f6c3d2019-01-22 11:29:35296- (instancetype)initWithSessionService:(SessionServiceIOS*)service
297 browserState:(ios::ChromeBrowserState*)browserState {
sdefresne67dfdd62016-12-19 12:43:12298 if ((self = [super init])) {
mrefaat294074dc2018-11-27 23:25:20299 _webStateListDelegate = std::make_unique<TabModelWebStateListDelegate>();
Jinho Bangb940d6f42018-01-18 22:39:04300 _webStateList = std::make_unique<WebStateList>(_webStateListDelegate.get());
sdefresne365a73a2017-03-14 15:07:13301
sdefresne67dfdd62016-12-19 12:43:12302 _browserState = browserState;
303 DCHECK(_browserState);
304
Sylvain Defresne94394332018-01-22 13:11:00305 _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
306
sdefresne67dfdd62016-12-19 12:43:12307 // 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 Bangb940d6f42018-01-18 22:39:04312 _tabUsageRecorder = std::make_unique<TabUsageRecorder>(
Sylvain Defresne890975f52017-08-24 17:42:26313 _webStateList.get(),
314 PrerenderServiceFactory::GetForBrowserState(browserState));
sdefresne67dfdd62016-12-19 12:43:12315 }
Sylvain Defresne72c1e992018-01-15 14:26:17316 _syncedWindowDelegate =
Jinho Bangb940d6f42018-01-18 22:39:04317 std::make_unique<TabModelSyncedWindowDelegate>(_webStateList.get());
sdefresne41971892017-02-23 17:24:14318
319 // There must be a valid session service defined to consume session windows.
320 DCHECK(service);
sdefresne4162a4dc2017-05-15 11:26:24321 _sessionService = service;
sdefresne41971892017-02-23 17:24:14322
sdefresne4162a4dc2017-05-15 11:26:24323 NSMutableArray<id<WebStateListObserving>>* retainedWebStateListObservers =
324 [[NSMutableArray alloc] init];
sdefresne3b0155e92017-04-12 15:18:53325
sdefresne4162a4dc2017-05-15 11:26:24326 TabModelClosingWebStateObserver* tabModelClosingWebStateObserver = [
327 [TabModelClosingWebStateObserver alloc]
328 initWithTabModel:self
329 restoreService:IOSChromeTabRestoreServiceFactory::GetForBrowserState(
330 _browserState)];
sdefresne3b0155e92017-04-12 15:18:53331 [retainedWebStateListObservers addObject:tabModelClosingWebStateObserver];
332
333 _webStateListObservers.push_back(
Sylvain Defresne94394332018-01-22 13:11:00334 std::make_unique<WebStateListObserverBridge>(self));
335
336 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04337 std::make_unique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53338 tabModelClosingWebStateObserver));
339
Jinho Bangb940d6f42018-01-18 22:39:04340 _webStateListObservers.push_back(std::make_unique<TabParentingObserver>());
sdefresne3b0155e92017-04-12 15:18:53341
sdefresne4162a4dc2017-05-15 11:26:24342 TabModelSelectedTabObserver* tabModelSelectedTabObserver =
343 [[TabModelSelectedTabObserver alloc] initWithTabModel:self];
sdefresne3b0155e92017-04-12 15:18:53344 [retainedWebStateListObservers addObject:tabModelSelectedTabObserver];
sdefresneee210332017-03-24 10:36:54345 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04346 std::make_unique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53347 tabModelSelectedTabObserver));
348
sdefresne2f7781c2017-03-02 19:12:46349 auto webStateListMetricsObserver =
Jinho Bangb940d6f42018-01-18 22:39:04350 std::make_unique<WebStateListMetricsObserver>();
sdefresne2f7781c2017-03-02 19:12:46351 _webStateListMetricsObserver = webStateListMetricsObserver.get();
sdefresneee210332017-03-24 10:36:54352 _webStateListObservers.push_back(std::move(webStateListMetricsObserver));
sdefresne41971892017-02-23 17:24:14353
sdefresneee210332017-03-24 10:36:54354 for (const auto& webStateListObserver : _webStateListObservers)
355 _webStateList->AddObserver(webStateListObserver.get());
sdefresne4162a4dc2017-05-15 11:26:24356 _retainedWebStateListObservers = [retainedWebStateListObservers copy];
sdefresne67dfdd62016-12-19 12:43:12357
sdefresne67dfdd62016-12-19 12:43:12358 // Register for resign active notification.
sdefresne44c09dc2017-02-07 11:13:08359 [[NSNotificationCenter defaultCenter]
360 addObserver:self
361 selector:@selector(willResignActive:)
362 name:UIApplicationWillResignActiveNotification
363 object:nil];
sdefresne67dfdd62016-12-19 12:43:12364 // Register for background notification.
sdefresne44c09dc2017-02-07 11:13:08365 [[NSNotificationCenter defaultCenter]
366 addObserver:self
367 selector:@selector(applicationDidEnterBackground:)
368 name:UIApplicationDidEnterBackgroundNotification
369 object:nil];
sdefresne8cfdf8f2017-01-11 18:22:14370
371 // Associate with ios::ChromeBrowserState.
Mohamad Ahmadie04d0952017-11-17 23:31:24372 TabModelList::RegisterTabModelWithChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12373 }
374 return self;
375}
376
Mohammad Refaat78374ac2019-06-28 17:37:46377- (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 {
sdefresne67dfdd62016-12-19 12:43:12384 web::NavigationManager::WebLoadParams params(URL);
385 params.referrer = referrer;
386 params.transition_type = transition;
Mohammad Refaat78374ac2019-06-28 17:37:46387 return [self insertWebStateWithLoadParams:params
388 opener:parentWebState
389 openedByDOM:openedByDOM
390 atIndex:index
391 inBackground:inBackground];
sdefresne67dfdd62016-12-19 12:43:12392}
393
Mohammad Refaat78374ac2019-06-28 17:37:46394- (web::WebState*)insertOpenByDOMWebStateWithOpener:
395 (web::WebState*)openerWebState {
Eugene Butfcdd8112018-02-06 02:28:04396 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 Refaat78374ac2019-06-28 17:37:46405 WebStateOpener(openerWebState));
Eugene Butfcdd8112018-02-06 02:28:04406
Mohammad Refaat78374ac2019-06-28 17:37:46407 return _webStateList->GetWebStateAt(insertedIndex);
Eugene Butfcdd8112018-02-06 02:28:04408}
409
Mohammad Refaat78374ac2019-06-28 17:37:46410- (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 {
sdefresnea6395912017-03-01 01:14:35416 DCHECK(_browserState);
Mark Coganfb485c332018-11-13 10:37:07417 DCHECK(index == TabModelConstants::kTabPositionAutomatically ||
418 index <= self.count);
sdefresne67dfdd62016-12-19 12:43:12419
Sylvain Defresne419a7a72017-08-09 16:27:37420 int insertionIndex = WebStateList::kInvalidIndex;
421 int insertionFlags = WebStateList::INSERT_NO_FLAGS;
422 if (index != TabModelConstants::kTabPositionAutomatically) {
sdefresne2c600c52017-04-04 16:49:59423 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne419a7a72017-08-09 16:27:37424 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;
sdefresne2c600c52017-04-04 16:49:59430 }
431
Sylvain Defresne2b834542017-08-10 14:50:02432 if (!inBackground) {
433 insertionFlags |= WebStateList::INSERT_ACTIVATE;
434 }
Sylvain Defresne419a7a72017-08-09 16:27:37435
Sylvain Defresne2b834542017-08-10 14:50:02436 web::WebState::CreateParams createParams(self.browserState);
437 createParams.created_with_opener = openedByDOM;
sdefresne2c600c52017-04-04 16:49:59438
Sylvain Defresne2b834542017-08-10 14:50:02439 std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams);
Sylvain Defresne419a7a72017-08-09 16:27:37440 webState->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresne2c600c52017-04-04 16:49:59441
Sylvain Defresne2b834542017-08-10 14:50:02442 insertionIndex = _webStateList->InsertWebState(
443 insertionIndex, std::move(webState), insertionFlags,
Mohammad Refaat78374ac2019-06-28 17:37:46444 WebStateOpener(parentWebState));
sdefresne7d699dd2017-04-05 13:05:23445
Mohammad Refaat78374ac2019-06-28 17:37:46446 return _webStateList->GetWebStateAt(insertionIndex);
sdefresne67dfdd62016-12-19 12:43:12447}
448
sdefresne67dfdd62016-12-19 12:43:12449- (void)closeTabAtIndex:(NSUInteger)index {
sdefresne2c600c52017-04-04 16:49:59450 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne6b776d72017-10-18 21:14:06451 _webStateList->CloseWebStateAt(static_cast<int>(index),
452 WebStateList::CLOSE_USER_ACTION);
sdefresne67dfdd62016-12-19 12:43:12453}
454
sdefresne67dfdd62016-12-19 12:43:12455- (void)closeAllTabs {
Sylvain Defresne6b776d72017-10-18 21:14:06456 _webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION);
sdefresne67dfdd62016-12-19 12:43:12457}
458
sdefresne67dfdd62016-12-19 12:43:12459- (void)resetSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46460 if (_webStateListMetricsObserver)
461 _webStateListMetricsObserver->ResetSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12462}
463
464- (void)recordSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46465 if (_webStateListMetricsObserver)
466 _webStateListMetricsObserver->RecordSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12467}
468
sdefresne67dfdd62016-12-19 12:43:12469- (void)setPrimary:(BOOL)primary {
Sylvain Defresne890975f52017-08-24 17:42:26470 if (_tabUsageRecorder) {
Mohammad Refaatcca3b06c2019-07-16 16:14:03471 _tabUsageRecorder->RecordPrimaryTabModelChange(
472 primary, self.webStateList->GetActiveWebState());
Sylvain Defresne890975f52017-08-24 17:42:26473 }
sdefresne67dfdd62016-12-19 12:43:12474}
475
sdefresne67dfdd62016-12-19 12:43:12476// NOTE: This can be called multiple times, so must be robust against that.
477- (void)browserStateDestroyed {
sdefresne05315352017-05-23 14:46:17478 if (!_browserState)
479 return;
480
sdefresne67dfdd62016-12-19 12:43:12481 [[NSNotificationCenter defaultCenter] removeObserver:self];
Mohamad Ahmadie04d0952017-11-17 23:31:24482 TabModelList::UnregisterTabModelFromChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12483 _browserState = nullptr;
sdefresne05315352017-05-23 14:46:17484
Sylvain Defresnee8ae6ad2017-09-14 14:22:10485 // Clear weak pointer to observers before destroying them.
sdefresne05315352017-05-23 14:46:17486 _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 Defresne6b776d72017-10-18 21:14:06494 _webStateList->CloseAllWebStates(WebStateList::CLOSE_NO_FLAGS);
sdefresne05315352017-05-23 14:46:17495 }
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 Defresne890975f52017-08-24 17:42:26505 _tabUsageRecorder.reset();
Sylvain Defresne94394332018-01-22 13:11:00506 _webStateObserver.reset();
sdefresne67dfdd62016-12-19 12:43:12507}
508
Mark Cogan30c24492019-01-16 16:52:58509#pragma mark - SessionWindowRestoring(public)
510
Mark Cogan30c24492019-01-16 16:52:58511- (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 Refaat3fb2b232019-08-07 20:49:53514 if ((!_webStateList->GetActiveWebState() && _webStateList->count()) ||
515 !_browserState)
Mark Cogan30c24492019-01-16 16:52:58516 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
sdefresne67dfdd62016-12-19 12:43:12528#pragma mark - Private methods
529
sdefresnedcc6c6e2017-04-19 15:49:02530- (SessionIOS*)sessionForSaving {
sdefresne67dfdd62016-12-19 12:43:12531 // 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).
sdefresne68f546a2017-04-20 10:54:03535 return [[SessionIOS alloc]
536 initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]];
sdefresne67dfdd62016-12-19 12:43:12537}
538
Kurt Horimoto070e40b2018-08-31 20:50:00539- (BOOL)isWebUsageEnabled {
540 DCHECK(_browserState);
541 return WebStateListWebUsageEnablerFactory::GetInstance()
542 ->GetForBrowserState(_browserState)
543 ->IsWebUsageEnabled();
544}
545
sdefresne44c09dc2017-02-07 11:13:08546- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window
Rohit Rao0d9f6c3d2019-01-22 11:29:35547 forInitialRestore:(BOOL)initialRestore {
sdefresne44c09dc2017-02-07 11:13:08548 DCHECK(_browserState);
Rohit Rao0d9f6c3d2019-01-22 11:29:35549
550 // It is only ok to pass a nil |window| during the initial restore.
551 DCHECK(window || initialRestore);
552
Mohammad Refaat89ddbf1f2019-05-23 12:12:48553 // 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 Defresnee8ae6ad2017-09-14 14:22:10559
sdefresne68f546a2017-04-20 10:54:03560 if (!window.sessions.count)
sdefresne44c09dc2017-02-07 11:13:08561 return NO;
562
sdefresne365a73a2017-03-14 15:07:13563 int oldCount = _webStateList->count();
sdefresne32c0c382017-02-17 16:37:26564 DCHECK_GE(oldCount, 0);
565
sdefresne08c0ab42017-04-05 12:49:43566 web::WebState::CreateParams createParams(_browserState);
567 DeserializeWebStateList(
sdefresnefd2aa652017-05-23 14:23:10568 _webStateList.get(), window,
Sylvain Defresne2b834542017-08-10 14:50:02569 base::BindRepeating(&web::WebState::CreateWithStorageSession,
570 createParams));
sdefresne08c0ab42017-04-05 12:49:43571
572 DCHECK_GT(_webStateList->count(), oldCount);
573 int restoredCount = _webStateList->count() - oldCount;
sdefresne68f546a2017-04-20 10:54:03574 DCHECK_EQ(window.sessions.count, static_cast<NSUInteger>(restoredCount));
sdefresne08c0ab42017-04-05 12:49:43575
sdefresnec92bada2017-02-08 14:43:49576 scoped_refptr<web::CertificatePolicyCache> policyCache =
577 web::BrowserState::GetCertificatePolicyCache(_browserState);
sdefresne44c09dc2017-02-07 11:13:08578
Sylvain Defresne890975f52017-08-24 17:42:26579 std::vector<web::WebState*> restoredWebStates;
580 if (_tabUsageRecorder)
581 restoredWebStates.reserve(window.sessions.count);
sdefresnee0a9ba62017-02-23 09:14:11582
sdefresne2c600c52017-04-04 16:49:59583 for (int index = oldCount; index < _webStateList->count(); ++index) {
584 web::WebState* webState = _webStateList->GetWebStateAt(index);
Sylvain Defresne7178d4c2017-09-14 13:22:37585 web::NavigationItem* visible_item =
586 webState->GetNavigationManager()->GetVisibleItem();
Gauthier Ambard32fe0dd2017-09-20 14:25:17587
588 if (!(visible_item &&
Sylvain Defresnec57fe762018-01-29 09:50:42589 visible_item->GetVirtualURL() == kChromeUINewTabURL)) {
Gauthier Ambard32fe0dd2017-09-20 14:25:17590 PagePlaceholderTabHelper::FromWebState(webState)
591 ->AddPlaceholderForNextNavigation();
592 }
593
Sylvain Defresne7178d4c2017-09-14 13:22:37594 if (visible_item && visible_item->GetVirtualURL().is_valid()) {
595 favicon::WebFaviconDriver::FromWebState(webState)->FetchFavicon(
596 visible_item->GetVirtualURL(), /*is_same_document=*/false);
597 }
598
sdefresne44c09dc2017-02-07 11:13:08599 // Restore the CertificatePolicyCache (note that webState is invalid after
sdefresnebf75f022017-03-07 17:48:35600 // passing it via move semantic to -initWithWebState:model:).
Eugene Butb56ecad2017-08-10 21:09:52601 UpdateCertificatePolicyCacheFromWebState(policyCache, webState);
Sylvain Defresne890975f52017-08-24 17:42:26602
603 if (_tabUsageRecorder)
604 restoredWebStates.push_back(webState);
sdefresne44c09dc2017-02-07 11:13:08605 }
sdefresnee0a9ba62017-02-23 09:14:11606
sdefresne44c09dc2017-02-07 11:13:08607 // If there was only one tab and it was the new tab page, clobber it.
608 BOOL closedNTPTab = NO;
609 if (oldCount == 1) {
Mohammad Refaatcca3b06c2019-07-16 16:14:03610 web::WebState* webState = _webStateList->GetWebStateAt(0);
kkhorimoto2a192b82017-06-01 20:11:08611 BOOL hasPendingLoad =
Mohammad Refaatcca3b06c2019-07-16 16:14:03612 webState->GetNavigationManager()->GetPendingItem() != nullptr;
Sylvain Defresnee7f2c8a2017-10-17 02:39:19613 if (!hasPendingLoad &&
Mohammad Refaatcca3b06c2019-07-16 16:14:03614 webState->GetLastCommittedURL() == kChromeUINewTabURL) {
615 _webStateList->CloseWebStateAt(0, WebStateList::CLOSE_USER_ACTION);
616
sdefresne44c09dc2017-02-07 11:13:08617 closedNTPTab = YES;
618 oldCount = 0;
619 }
620 }
Sylvain Defresne890975f52017-08-24 17:42:26621 if (_tabUsageRecorder) {
Mohammad Refaatcca3b06c2019-07-16 16:14:03622 _tabUsageRecorder->InitialRestoredTabs(_webStateList->GetActiveWebState(),
Sylvain Defresne890975f52017-08-24 17:42:26623 restoredWebStates);
624 }
Sylvain Defresnee8ae6ad2017-09-14 14:22:10625
Rohit Rao0d9f6c3d2019-01-22 11:29:35626 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
sdefresne44c09dc2017-02-07 11:13:08632 return closedNTPTab;
633}
634
sdefresne67dfdd62016-12-19 12:43:12635#pragma mark - Notification Handlers
636
637// Called when UIApplicationWillResignActiveNotification is received.
638- (void)willResignActive:(NSNotification*)notify {
Mohammad Refaatcca3b06c2019-07-16 16:14:03639 if (self.webUsageEnabled && _webStateList->GetActiveWebState()) {
mrefaat2e8bafb2019-02-13 03:17:57640 NSString* tabId =
Mohammad Refaatcca3b06c2019-07-16 16:14:03641 TabIdTabHelper::FromWebState(_webStateList->GetActiveWebState())
642 ->tab_id();
Sylvain Defresne528c17b2017-06-08 15:00:21643 [SnapshotCacheFactory::GetForBrowserState(_browserState)
mrefaat2e8bafb2019-02-13 03:17:57644 willBeSavedGreyWhenBackgrounding:tabId];
sdefresne67dfdd62016-12-19 12:43:12645 }
646}
647
648// Called when UIApplicationDidEnterBackgroundNotification is received.
649- (void)applicationDidEnterBackground:(NSNotification*)notify {
650 if (!_browserState)
651 return;
sdefresnec92bada2017-02-08 14:43:49652
sdefresne67dfdd62016-12-19 12:43:12653 // Evict all the certificate policies except for the current entries of the
654 // active sessions.
sdefresnec92bada2017-02-08 14:43:49655 CleanCertificatePolicyCache(
656 &_clearPoliciesTaskTracker,
Eric Seckler217006b2018-09-11 15:21:02657 base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}),
sdefresne32c0c382017-02-17 16:37:26658 web::BrowserState::GetCertificatePolicyCache(_browserState),
sdefresne365a73a2017-03-14 15:07:13659 _webStateList.get());
sdefresne67dfdd62016-12-19 12:43:12660
sdefresne67dfdd62016-12-19 12:43:12661 // 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 Refaatcca3b06c2019-07-16 16:14:03666 if (self.webUsageEnabled && _webStateList->GetActiveWebState()) {
mrefaat2e8bafb2019-02-13 03:17:57667 NSString* tabId =
Mohammad Refaatcca3b06c2019-07-16 16:14:03668 TabIdTabHelper::FromWebState(_webStateList->GetActiveWebState())
669 ->tab_id();
mrefaat2e8bafb2019-02-13 03:17:57670
Sylvain Defresne528c17b2017-06-08 15:00:21671 [SnapshotCacheFactory::GetForBrowserState(_browserState)
mrefaat2e8bafb2019-02-13 03:17:57672 saveGreyInBackgroundForSessionID:tabId];
sdefresne67dfdd62016-12-19 12:43:12673 }
674}
675
Sylvain Defresne94394332018-01-22 13:11:00676#pragma mark - CRWWebStateObserver
677
678- (void)webState:(web::WebState*)webState
Eugene But5c6a82d2019-01-10 17:15:32679 didFinishNavigation:(web::NavigationContext*)navigation {
Sylvain Defresne94394332018-01-22 13:11:00680
Eugene But5c6a82d2019-01-10 17:15:32681 if (!navigation->IsSameDocument() && navigation->HasCommitted() &&
682 !self.offTheRecord) {
Eugene Butb117c322019-01-07 21:00:28683 int tabCount = static_cast<int>(self.count);
684 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tabCount, 1, 200, 50);
Sylvain Defresne94394332018-01-22 13:11:00685 }
Sylvain Defresne94394332018-01-22 13:11:00686}
687
688- (void)webState:(web::WebState*)webState
689 didStartNavigation:(web::NavigationContext*)navigation {
Sylvain Defresne94394332018-01-22 13:11:00690
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 Defresne94394332018-01-22 13:11:00706 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 But9c3e90c2019-01-04 20:45:04717 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 Defresne94394332018-01-22 13:11:00728 [[OmniboxGeolocationController sharedInstance]
729 addLocationToNavigationItem:navigationItem
730 browserState:ios::ChromeBrowserState::FromBrowserState(
731 webState->GetBrowserState())];
732}
733
Sylvain Defresne94394332018-01-22 13:11:00734- (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
Sylvain Defresne94394332018-01-22 13:11:00735 RecordInterfaceOrientationMetric();
736 RecordMainFrameNavigationMetric(webState);
737
mrefaatc977e372019-02-04 22:19:18738 [[OmniboxGeolocationController sharedInstance]
739 finishPageLoadForWebState:webState
740 loadSuccess:success];
Sylvain Defresne94394332018-01-22 13:11:00741}
742
Sylvain Defresne94394332018-01-22 13:11:00743- (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 Defresne94394332018-01-22 13:11:00750#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
sdefresne67dfdd62016-12-19 12:43:12777@end