blob: 04b31eda3cfc965ce64e734d490b3a631d5f089a [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"
sdefresne67dfdd62016-12-19 12:43:1212#include "base/logging.h"
sdefresne2c600c52017-04-04 16:49:5913#import "base/mac/foundation_util.h"
asvitkinef1899e32017-01-27 16:30:2914#include "base/metrics/histogram_macros.h"
sdefresne67dfdd62016-12-19 12:43:1215#include "base/metrics/user_metrics_action.h"
16#include "base/strings/sys_string_conversions.h"
sdefresnec92bada2017-02-08 14:43:4917#include "base/task/cancelable_task_tracker.h"
sdefresne67dfdd62016-12-19 12:43:1218#include "components/sessions/core/serialized_navigation_entry.h"
19#include "components/sessions/core/session_id.h"
20#include "components/sessions/core/tab_restore_service.h"
21#include "components/sessions/ios/ios_live_tab.h"
22#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
23#include "ios/chrome/browser/chrome_url_constants.h"
24#import "ios/chrome/browser/chrome_url_util.h"
25#import "ios/chrome/browser/metrics/tab_usage_recorder.h"
Sylvain Defresne890975f52017-08-24 17:42:2626#import "ios/chrome/browser/prerender/prerender_service_factory.h"
sdefresne67dfdd62016-12-19 12:43:1227#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
sdefresnedcc6c6e2017-04-19 15:49:0228#import "ios/chrome/browser/sessions/session_ios.h"
sdefresneda974c82017-04-06 17:24:3929#import "ios/chrome/browser/sessions/session_service_ios.h"
sdefresne56528152017-03-22 17:03:2830#import "ios/chrome/browser/sessions/session_window_ios.h"
sdefresne67dfdd62016-12-19 12:43:1231#import "ios/chrome/browser/snapshots/snapshot_cache.h"
Sylvain Defresne528c17b2017-06-08 15:00:2132#import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
sdefresne2f7781c2017-03-02 19:12:4633#import "ios/chrome/browser/snapshots/snapshot_cache_web_state_list_observer.h"
sdefresne67dfdd62016-12-19 12:43:1234#include "ios/chrome/browser/tab_parenting_global_observer.h"
sdefresne32c0c382017-02-17 16:37:2635#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
sdefresne67dfdd62016-12-19 12:43:1236#import "ios/chrome/browser/tabs/tab.h"
sdefresne2c600c52017-04-04 16:49:5937#import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h"
sdefresne8cfdf8f2017-01-11 18:22:1438#import "ios/chrome/browser/tabs/tab_model_list.h"
Sylvain Defresne2b834542017-08-10 14:50:0239#import "ios/chrome/browser/tabs/tab_model_notification_observer.h"
sdefresne0a762e42017-02-14 11:11:2540#import "ios/chrome/browser/tabs/tab_model_observers.h"
sdefresne39bfaab2017-02-20 11:06:3641#import "ios/chrome/browser/tabs/tab_model_observers_bridge.h"
sdefresne2f7781c2017-03-02 19:12:4642#import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h"
sdefresne67dfdd62016-12-19 12:43:1243#import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h"
sdefresne365a73a2017-03-14 15:07:1344#import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.h"
sdefresne39bfaab2017-02-20 11:06:3645#import "ios/chrome/browser/tabs/tab_parenting_observer.h"
Eugene Butb56ecad2017-08-10 21:09:5246#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
sdefresne62a00bb2017-04-10 15:36:0547#import "ios/chrome/browser/web_state_list/web_state_list.h"
48#import "ios/chrome/browser/web_state_list/web_state_list_fast_enumeration_helper.h"
49#import "ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h"
50#import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
51#import "ios/chrome/browser/web_state_list/web_state_list_serialization.h"
52#import "ios/chrome/browser/web_state_list/web_state_opener.h"
sdefresne67dfdd62016-12-19 12:43:1253#include "ios/web/public/browser_state.h"
54#include "ios/web/public/certificate_policy_cache.h"
55#include "ios/web/public/navigation_item.h"
56#import "ios/web/public/navigation_manager.h"
sdefresne2c600c52017-04-04 16:49:5957#import "ios/web/public/serializable_user_data_manager.h"
kkhorimotod3a3986f2017-04-05 02:21:4158#include "ios/web/public/web_state/session_certificate_policy_cache.h"
sdefresne67dfdd62016-12-19 12:43:1259#include "ios/web/public/web_thread.h"
60#import "ios/web/web_state/ui/crw_web_controller.h"
sdefresne67dfdd62016-12-19 12:43:1261#include "url/gurl.h"
62
sdefresne4162a4dc2017-05-15 11:26:2463#if !defined(__has_feature) || !__has_feature(objc_arc)
64#error "This file requires ARC support."
65#endif
66
sdefresne67dfdd62016-12-19 12:43:1267NSString* const kTabModelTabWillStartLoadingNotification =
68 @"kTabModelTabWillStartLoadingNotification";
sdefresne67dfdd62016-12-19 12:43:1269NSString* const kTabModelTabDidStartLoadingNotification =
70 @"kTabModelTabDidStartLoadingNotification";
71NSString* const kTabModelTabDidFinishLoadingNotification =
72 @"kTabModelTabDidFinishLoadingNotification";
73NSString* const kTabModelAllTabsDidCloseNotification =
74 @"kTabModelAllTabsDidCloseNotification";
75NSString* const kTabModelTabDeselectedNotification =
76 @"kTabModelTabDeselectedNotification";
77NSString* const kTabModelNewTabWillOpenNotification =
78 @"kTabModelNewTabWillOpenNotification";
79NSString* const kTabModelTabKey = @"tab";
80NSString* const kTabModelPageLoadSuccess = @"pageLoadSuccess";
81NSString* const kTabModelOpenInBackgroundKey = @"shouldOpenInBackground";
82
83namespace {
84
85// Updates CRWSessionCertificatePolicyManager's certificate policy cache.
sdefresnec92bada2017-02-08 14:43:4986void UpdateCertificatePolicyCacheFromWebState(
87 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
kkhorimotod3a3986f2017-04-05 02:21:4188 const web::WebState* web_state) {
sdefresne4c8bf632017-01-26 15:46:4789 DCHECK(web_state);
sdefresnec92bada2017-02-08 14:43:4990 DCHECK_CURRENTLY_ON(web::WebThread::UI);
kkhorimotod3a3986f2017-04-05 02:21:4191 web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache(
92 policy_cache);
sdefresne67dfdd62016-12-19 12:43:1293}
94
sdefresne32c0c382017-02-17 16:37:2695// Populates the certificate policy cache based on the WebStates of
96// |web_state_list|.
sdefresnec92bada2017-02-08 14:43:4997void RestoreCertificatePolicyCacheFromModel(
98 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:2699 WebStateList* web_state_list) {
sdefresnec92bada2017-02-08 14:43:49100 DCHECK_CURRENTLY_ON(web::WebThread::UI);
sdefresne32c0c382017-02-17 16:37:26101 for (int index = 0; index < web_state_list->count(); ++index) {
102 UpdateCertificatePolicyCacheFromWebState(
103 policy_cache, web_state_list->GetWebStateAt(index));
104 }
sdefresne67dfdd62016-12-19 12:43:12105}
106
sdefresnec92bada2017-02-08 14:43:49107// Scrubs the certificate policy cache of all certificates policies except
sdefresne32c0c382017-02-17 16:37:26108// those for the current entries in |web_state_list|.
sdefresne67dfdd62016-12-19 12:43:12109void CleanCertificatePolicyCache(
sdefresnec92bada2017-02-08 14:43:49110 base::CancelableTaskTracker* task_tracker,
111 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
112 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:26113 WebStateList* web_state_list) {
sdefresne67dfdd62016-12-19 12:43:12114 DCHECK(policy_cache);
sdefresne32c0c382017-02-17 16:37:26115 DCHECK(web_state_list);
sdefresnec92bada2017-02-08 14:43:49116 DCHECK_CURRENTLY_ON(web::WebThread::UI);
117 task_tracker->PostTaskAndReply(
118 task_runner.get(), FROM_HERE,
119 base::Bind(&web::CertificatePolicyCache::ClearCertificatePolicies,
120 policy_cache),
121 base::Bind(&RestoreCertificatePolicyCacheFromModel, policy_cache,
sdefresne32c0c382017-02-17 16:37:26122 base::Unretained(web_state_list)));
sdefresne67dfdd62016-12-19 12:43:12123}
124
sdefresne67dfdd62016-12-19 12:43:12125} // anonymous namespace
126
sdefresne32c0c382017-02-17 16:37:26127@interface TabModelWebStateProxyFactory : NSObject<WebStateProxyFactory>
128@end
129
130@implementation TabModelWebStateProxyFactory
131
132- (id)proxyForWebState:(web::WebState*)webState {
133 return LegacyTabHelper::GetTabForWebState(webState);
134}
135
136@end
137
Sylvain Defresne890975f52017-08-24 17:42:26138@interface TabModel () {
sdefresne365a73a2017-03-14 15:07:13139 // Delegate for the WebStateList.
140 std::unique_ptr<WebStateListDelegate> _webStateListDelegate;
141
sdefresne32c0c382017-02-17 16:37:26142 // Underlying shared model implementation.
sdefresne365a73a2017-03-14 15:07:13143 std::unique_ptr<WebStateList> _webStateList;
sdefresne32c0c382017-02-17 16:37:26144
145 // Helper providing NSFastEnumeration implementation over the WebStateList.
sdefresne4a0a4e852017-04-19 13:16:14146 std::unique_ptr<WebStateListFastEnumerationHelper> _fastEnumerationHelper;
sdefresne32c0c382017-02-17 16:37:26147
sdefresneee210332017-03-24 10:36:54148 // WebStateListObservers reacting to modifications of the model (may send
149 // notification, translate and forward events, update metrics, ...).
150 std::vector<std::unique_ptr<WebStateListObserver>> _webStateListObservers;
sdefresne39bfaab2017-02-20 11:06:36151
sdefresne3b0155e92017-04-12 15:18:53152 // Strong references to id<WebStateListObserving> wrapped by non-owning
153 // WebStateListObserverBridges.
sdefresne4162a4dc2017-05-15 11:26:24154 NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers;
sdefresne3b0155e92017-04-12 15:18:53155
sdefresne67dfdd62016-12-19 12:43:12156 // The delegate for sync.
157 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate;
sdefresne67dfdd62016-12-19 12:43:12158
159 // Counters for metrics.
sdefresne2f7781c2017-03-02 19:12:46160 WebStateListMetricsObserver* _webStateListMetricsObserver;
sdefresne67dfdd62016-12-19 12:43:12161
162 // Backs up property with the same name.
163 std::unique_ptr<TabUsageRecorder> _tabUsageRecorder;
164 // Backs up property with the same name.
165 const SessionID _sessionID;
166 // Saves session's state.
sdefresne4162a4dc2017-05-15 11:26:24167 SessionServiceIOS* _sessionService;
sdefresne67dfdd62016-12-19 12:43:12168 // List of TabModelObservers.
sdefresne4162a4dc2017-05-15 11:26:24169 TabModelObservers* _observers;
sdefresnec92bada2017-02-08 14:43:49170
171 // Used to ensure thread-safety of the certificate policy management code.
172 base::CancelableTaskTracker _clearPoliciesTaskTracker;
sdefresne67dfdd62016-12-19 12:43:12173}
174
175// Session window for the contents of the tab model.
sdefresnedcc6c6e2017-04-19 15:49:02176@property(nonatomic, readonly) SessionIOS* sessionForSaving;
sdefresne67dfdd62016-12-19 12:43:12177
sdefresne44c09dc2017-02-07 11:13:08178// Helper method to restore a saved session and control if the state should
179// be persisted or not. Used to implement the public -restoreSessionWindow:
180// method and restoring session in the initialiser.
181- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window
182 persistState:(BOOL)persistState;
183
sdefresne67dfdd62016-12-19 12:43:12184@end
185
186@implementation TabModel
187
188@synthesize browserState = _browserState;
189@synthesize sessionID = _sessionID;
sdefresnefd2aa652017-05-23 14:23:10190@synthesize webUsageEnabled = _webUsageEnabled;
sdefresne67dfdd62016-12-19 12:43:12191
192#pragma mark - Overriden
193
194- (void)dealloc {
sdefresne67dfdd62016-12-19 12:43:12195 // browserStateDestroyed should always have been called before destruction.
196 DCHECK(!_browserState);
197
sdefresne05315352017-05-23 14:46:17198 // Make sure the observers do clean after themselves.
199 DCHECK([_observers empty]);
sdefresne67dfdd62016-12-19 12:43:12200}
201
202#pragma mark - Public methods
203
204- (Tab*)currentTab {
sdefresne365a73a2017-03-14 15:07:13205 web::WebState* webState = _webStateList->GetActiveWebState();
sdefresne2f7781c2017-03-02 19:12:46206 return webState ? LegacyTabHelper::GetTabForWebState(webState) : nil;
sdefresne67dfdd62016-12-19 12:43:12207}
208
209- (void)setCurrentTab:(Tab*)newTab {
sdefresne365a73a2017-03-14 15:07:13210 int indexOfTab = _webStateList->GetIndexOfWebState(newTab.webState);
sdefresne2f7781c2017-03-02 19:12:46211 DCHECK_NE(indexOfTab, WebStateList::kInvalidIndex);
sdefresne365a73a2017-03-14 15:07:13212 _webStateList->ActivateWebStateAt(indexOfTab);
sdefresne67dfdd62016-12-19 12:43:12213}
214
215- (TabModelSyncedWindowDelegate*)syncedWindowDelegate {
216 return _syncedWindowDelegate.get();
217}
218
219- (TabUsageRecorder*)tabUsageRecorder {
220 return _tabUsageRecorder.get();
221}
222
223- (BOOL)isOffTheRecord {
224 return _browserState && _browserState->IsOffTheRecord();
225}
226
227- (BOOL)isEmpty {
sdefresne365a73a2017-03-14 15:07:13228 return _webStateList->empty();
sdefresne67dfdd62016-12-19 12:43:12229}
230
sdefresne297553e52017-01-25 17:09:37231- (NSUInteger)count {
sdefresne365a73a2017-03-14 15:07:13232 DCHECK_GE(_webStateList->count(), 0);
233 return static_cast<NSUInteger>(_webStateList->count());
sdefresne297553e52017-01-25 17:09:37234}
235
sdefresneee210332017-03-24 10:36:54236- (WebStateList*)webStateList {
237 return _webStateList.get();
238}
239
sdefresne67dfdd62016-12-19 12:43:12240- (instancetype)initWithSessionWindow:(SessionWindowIOS*)window
241 sessionService:(SessionServiceIOS*)service
242 browserState:(ios::ChromeBrowserState*)browserState {
243 if ((self = [super init])) {
sdefresne4162a4dc2017-05-15 11:26:24244 _observers = [TabModelObservers observers];
sdefresne67dfdd62016-12-19 12:43:12245
sdefresne365a73a2017-03-14 15:07:13246 _webStateListDelegate =
247 base::MakeUnique<TabModelWebStateListDelegate>(self);
sdefresne2c600c52017-04-04 16:49:59248 _webStateList = base::MakeUnique<WebStateList>(_webStateListDelegate.get());
sdefresne365a73a2017-03-14 15:07:13249
sdefresne4a0a4e852017-04-19 13:16:14250 _fastEnumerationHelper =
251 base::MakeUnique<WebStateListFastEnumerationHelper>(
252 _webStateList.get(), [[TabModelWebStateProxyFactory alloc] init]);
sdefresne32c0c382017-02-17 16:37:26253
sdefresne67dfdd62016-12-19 12:43:12254 _browserState = browserState;
255 DCHECK(_browserState);
256
sdefresne67dfdd62016-12-19 12:43:12257 // Normal browser states are the only ones to get tab restore. Tab sync
258 // handles incognito browser states by filtering on profile, so it's
259 // important to the backend code to always have a sync window delegate.
260 if (!_browserState->IsOffTheRecord()) {
261 // Set up the usage recorder before tabs are created.
Sylvain Defresne890975f52017-08-24 17:42:26262 _tabUsageRecorder = base::MakeUnique<TabUsageRecorder>(
263 _webStateList.get(),
264 PrerenderServiceFactory::GetForBrowserState(browserState));
sdefresne67dfdd62016-12-19 12:43:12265 }
sdefresne91e46a62017-05-11 16:33:50266 _syncedWindowDelegate = base::MakeUnique<TabModelSyncedWindowDelegate>(
267 _webStateList.get(), _sessionID);
sdefresne41971892017-02-23 17:24:14268
269 // There must be a valid session service defined to consume session windows.
270 DCHECK(service);
sdefresne4162a4dc2017-05-15 11:26:24271 _sessionService = service;
sdefresne41971892017-02-23 17:24:14272
sdefresne4162a4dc2017-05-15 11:26:24273 NSMutableArray<id<WebStateListObserving>>* retainedWebStateListObservers =
274 [[NSMutableArray alloc] init];
sdefresne3b0155e92017-04-12 15:18:53275
sdefresne4162a4dc2017-05-15 11:26:24276 TabModelClosingWebStateObserver* tabModelClosingWebStateObserver = [
277 [TabModelClosingWebStateObserver alloc]
278 initWithTabModel:self
279 restoreService:IOSChromeTabRestoreServiceFactory::GetForBrowserState(
280 _browserState)];
sdefresne3b0155e92017-04-12 15:18:53281 [retainedWebStateListObservers addObject:tabModelClosingWebStateObserver];
282
283 _webStateListObservers.push_back(
284 base::MakeUnique<WebStateListObserverBridge>(
285 tabModelClosingWebStateObserver));
286
Sylvain Defresne528c17b2017-06-08 15:00:21287 SnapshotCache* snapshotCache =
288 SnapshotCacheFactory::GetForBrowserState(_browserState);
289 if (snapshotCache) {
290 _webStateListObservers.push_back(
291 base::MakeUnique<SnapshotCacheWebStateListObserver>(snapshotCache));
292 }
293
sdefresneee210332017-03-24 10:36:54294 _webStateListObservers.push_back(base::MakeUnique<TabParentingObserver>());
sdefresne3b0155e92017-04-12 15:18:53295
sdefresne4162a4dc2017-05-15 11:26:24296 TabModelSelectedTabObserver* tabModelSelectedTabObserver =
297 [[TabModelSelectedTabObserver alloc] initWithTabModel:self];
sdefresne3b0155e92017-04-12 15:18:53298 [retainedWebStateListObservers addObject:tabModelSelectedTabObserver];
sdefresneee210332017-03-24 10:36:54299 _webStateListObservers.push_back(
300 base::MakeUnique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53301 tabModelSelectedTabObserver));
302
sdefresne4162a4dc2017-05-15 11:26:24303 TabModelObserversBridge* tabModelObserversBridge =
sdefresne3b0155e92017-04-12 15:18:53304 [[TabModelObserversBridge alloc] initWithTabModel:self
sdefresne4162a4dc2017-05-15 11:26:24305 tabModelObservers:_observers];
sdefresne3b0155e92017-04-12 15:18:53306 [retainedWebStateListObservers addObject:tabModelObserversBridge];
sdefresneee210332017-03-24 10:36:54307 _webStateListObservers.push_back(
sdefresne3b0155e92017-04-12 15:18:53308 base::MakeUnique<WebStateListObserverBridge>(tabModelObserversBridge));
sdefresne2f7781c2017-03-02 19:12:46309
310 auto webStateListMetricsObserver =
311 base::MakeUnique<WebStateListMetricsObserver>();
312 _webStateListMetricsObserver = webStateListMetricsObserver.get();
sdefresneee210332017-03-24 10:36:54313 _webStateListObservers.push_back(std::move(webStateListMetricsObserver));
sdefresne41971892017-02-23 17:24:14314
Sylvain Defresne2b834542017-08-10 14:50:02315 _webStateListObservers.push_back(
316 base::MakeUnique<TabModelNotificationObserver>(self));
317
sdefresneee210332017-03-24 10:36:54318 for (const auto& webStateListObserver : _webStateListObservers)
319 _webStateList->AddObserver(webStateListObserver.get());
sdefresne4162a4dc2017-05-15 11:26:24320 _retainedWebStateListObservers = [retainedWebStateListObservers copy];
sdefresne67dfdd62016-12-19 12:43:12321
sdefresne67dfdd62016-12-19 12:43:12322 if (window) {
sdefresne44c09dc2017-02-07 11:13:08323 DCHECK([_observers empty]);
324 // Restore the session and reset the session metrics (as the event have
325 // not been generated by the user but by a cold start cycle).
326 [self restoreSessionWindow:window persistState:NO];
327 [self resetSessionMetrics];
sdefresne67dfdd62016-12-19 12:43:12328 }
329
sdefresne67dfdd62016-12-19 12:43:12330 // Register for resign active notification.
sdefresne44c09dc2017-02-07 11:13:08331 [[NSNotificationCenter defaultCenter]
332 addObserver:self
333 selector:@selector(willResignActive:)
334 name:UIApplicationWillResignActiveNotification
335 object:nil];
sdefresne67dfdd62016-12-19 12:43:12336 // Register for background notification.
sdefresne44c09dc2017-02-07 11:13:08337 [[NSNotificationCenter defaultCenter]
338 addObserver:self
339 selector:@selector(applicationDidEnterBackground:)
340 name:UIApplicationDidEnterBackgroundNotification
341 object:nil];
sdefresne67dfdd62016-12-19 12:43:12342 // Register for foregrounding notification.
sdefresne44c09dc2017-02-07 11:13:08343 [[NSNotificationCenter defaultCenter]
344 addObserver:self
345 selector:@selector(applicationWillEnterForeground:)
346 name:UIApplicationWillEnterForegroundNotification
347 object:nil];
sdefresne8cfdf8f2017-01-11 18:22:14348
349 // Associate with ios::ChromeBrowserState.
350 RegisterTabModelWithChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12351 }
352 return self;
353}
354
355- (instancetype)init {
356 NOTREACHED();
357 return nil;
358}
359
360- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window {
sdefresne44c09dc2017-02-07 11:13:08361 return [self restoreSessionWindow:window persistState:YES];
sdefresne67dfdd62016-12-19 12:43:12362}
363
364- (void)saveSessionImmediately:(BOOL)immediately {
365 // Do nothing if there are tabs in the model but no selected tab. This is
366 // a transitional state.
sdefresne365a73a2017-03-14 15:07:13367 if ((!self.currentTab && _webStateList->count()) || !_browserState)
sdefresne67dfdd62016-12-19 12:43:12368 return;
sdefresne80a544622017-04-12 12:44:27369 NSString* statePath =
370 base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe());
Sylvain Defresne5fbebad2017-06-01 11:23:53371 __weak TabModel* weakSelf = self;
372 SessionIOSFactory sessionFactory = ^{
373 return weakSelf.sessionForSaving;
374 };
375 [_sessionService saveSession:sessionFactory
sdefresnedcc6c6e2017-04-19 15:49:02376 directory:statePath
377 immediately:immediately];
sdefresne67dfdd62016-12-19 12:43:12378}
379
380- (Tab*)tabAtIndex:(NSUInteger)index {
sdefresne32c0c382017-02-17 16:37:26381 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
382 return LegacyTabHelper::GetTabForWebState(
sdefresne365a73a2017-03-14 15:07:13383 _webStateList->GetWebStateAt(static_cast<int>(index)));
sdefresne67dfdd62016-12-19 12:43:12384}
385
386- (NSUInteger)indexOfTab:(Tab*)tab {
sdefresne365a73a2017-03-14 15:07:13387 int index = _webStateList->GetIndexOfWebState(tab.webState);
sdefresne32c0c382017-02-17 16:37:26388 if (index == WebStateList::kInvalidIndex)
389 return NSNotFound;
390
391 DCHECK_GE(index, 0);
392 return static_cast<NSUInteger>(index);
sdefresne67dfdd62016-12-19 12:43:12393}
394
sdefresne67dfdd62016-12-19 12:43:12395- (Tab*)openerOfTab:(Tab*)tab {
sdefresne365a73a2017-03-14 15:07:13396 int index = _webStateList->GetIndexOfWebState(tab.webState);
sdefresnee0a9ba62017-02-23 09:14:11397 if (index == WebStateList::kInvalidIndex)
sdefresne67dfdd62016-12-19 12:43:12398 return nil;
sdefresnee0a9ba62017-02-23 09:14:11399
sdefresne500f5222017-03-24 14:01:40400 WebStateOpener opener = _webStateList->GetOpenerOfWebStateAt(index);
401 return opener.opener ? LegacyTabHelper::GetTabForWebState(opener.opener)
402 : nil;
sdefresne67dfdd62016-12-19 12:43:12403}
404
sdefresnea6395912017-03-01 01:14:35405- (Tab*)insertTabWithURL:(const GURL&)URL
406 referrer:(const web::Referrer&)referrer
407 transition:(ui::PageTransition)transition
408 opener:(Tab*)parentTab
409 openedByDOM:(BOOL)openedByDOM
410 atIndex:(NSUInteger)index
411 inBackground:(BOOL)inBackground {
sdefresne67dfdd62016-12-19 12:43:12412 web::NavigationManager::WebLoadParams params(URL);
413 params.referrer = referrer;
414 params.transition_type = transition;
sdefresnea6395912017-03-01 01:14:35415 return [self insertTabWithLoadParams:params
416 opener:parentTab
417 openedByDOM:openedByDOM
418 atIndex:index
419 inBackground:inBackground];
sdefresne67dfdd62016-12-19 12:43:12420}
421
sdefresnea6395912017-03-01 01:14:35422- (Tab*)insertTabWithLoadParams:
sdefresne2c600c52017-04-04 16:49:59423 (const web::NavigationManager::WebLoadParams&)loadParams
sdefresnea6395912017-03-01 01:14:35424 opener:(Tab*)parentTab
425 openedByDOM:(BOOL)openedByDOM
426 atIndex:(NSUInteger)index
427 inBackground:(BOOL)inBackground {
428 DCHECK(_browserState);
sdefresne67dfdd62016-12-19 12:43:12429
Sylvain Defresne419a7a72017-08-09 16:27:37430 int insertionIndex = WebStateList::kInvalidIndex;
431 int insertionFlags = WebStateList::INSERT_NO_FLAGS;
432 if (index != TabModelConstants::kTabPositionAutomatically) {
sdefresne2c600c52017-04-04 16:49:59433 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne419a7a72017-08-09 16:27:37434 insertionIndex = static_cast<int>(index);
435 insertionFlags |= WebStateList::INSERT_FORCE_INDEX;
436 } else if (!ui::PageTransitionCoreTypeIs(loadParams.transition_type,
437 ui::PAGE_TRANSITION_LINK)) {
438 insertionIndex = _webStateList->count();
439 insertionFlags |= WebStateList::INSERT_FORCE_INDEX;
sdefresne2c600c52017-04-04 16:49:59440 }
441
Sylvain Defresne2b834542017-08-10 14:50:02442 if (!inBackground) {
443 insertionFlags |= WebStateList::INSERT_ACTIVATE;
444 }
Sylvain Defresne419a7a72017-08-09 16:27:37445
Sylvain Defresne2b834542017-08-10 14:50:02446 web::WebState::CreateParams createParams(self.browserState);
447 createParams.created_with_opener = openedByDOM;
sdefresne2c600c52017-04-04 16:49:59448
Sylvain Defresne2b834542017-08-10 14:50:02449 std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams);
Sylvain Defresne419a7a72017-08-09 16:27:37450 webState->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresne2c600c52017-04-04 16:49:59451
Sylvain Defresne2b834542017-08-10 14:50:02452 insertionIndex = _webStateList->InsertWebState(
453 insertionIndex, std::move(webState), insertionFlags,
454 WebStateOpener(parentTab.webState));
sdefresne7d699dd2017-04-05 13:05:23455
Sylvain Defresne2b834542017-08-10 14:50:02456 return LegacyTabHelper::GetTabForWebState(
457 _webStateList->GetWebStateAt(insertionIndex));
sdefresne67dfdd62016-12-19 12:43:12458}
459
sdefresne67dfdd62016-12-19 12:43:12460- (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex {
sdefresne32c0c382017-02-17 16:37:26461 DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX));
sdefresne365a73a2017-03-14 15:07:13462 int fromIndex = _webStateList->GetIndexOfWebState(tab.webState);
463 _webStateList->MoveWebStateAt(fromIndex, static_cast<int>(toIndex));
sdefresne67dfdd62016-12-19 12:43:12464}
465
sdefresne67dfdd62016-12-19 12:43:12466- (void)closeTabAtIndex:(NSUInteger)index {
sdefresne2c600c52017-04-04 16:49:59467 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
468 _webStateList->CloseWebStateAt(static_cast<int>(index));
sdefresne67dfdd62016-12-19 12:43:12469}
470
471- (void)closeTab:(Tab*)tab {
sdefresne2c600c52017-04-04 16:49:59472 [self closeTabAtIndex:[self indexOfTab:tab]];
sdefresne67dfdd62016-12-19 12:43:12473}
474
475- (void)closeAllTabs {
sdefresne2c600c52017-04-04 16:49:59476 _webStateList->CloseAllWebStates();
sdefresne67dfdd62016-12-19 12:43:12477 [[NSNotificationCenter defaultCenter]
478 postNotificationName:kTabModelAllTabsDidCloseNotification
479 object:self];
480}
481
482- (void)haltAllTabs {
sdefresne32c0c382017-02-17 16:37:26483 for (Tab* tab in self) {
sdefresne67dfdd62016-12-19 12:43:12484 [tab terminateNetworkActivity];
485 }
486}
487
488- (void)notifyTabChanged:(Tab*)tab {
489 [_observers tabModel:self didChangeTab:tab];
490}
491
492- (void)addObserver:(id<TabModelObserver>)observer {
493 [_observers addObserver:observer];
494}
495
496- (void)removeObserver:(id<TabModelObserver>)observer {
497 [_observers removeObserver:observer];
498}
499
500- (void)resetSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46501 if (_webStateListMetricsObserver)
502 _webStateListMetricsObserver->ResetSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12503}
504
505- (void)recordSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46506 if (_webStateListMetricsObserver)
507 _webStateListMetricsObserver->RecordSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12508}
509
510- (void)notifyTabSnapshotChanged:(Tab*)tab withImage:(UIImage*)image {
511 DCHECK([NSThread isMainThread]);
512 [_observers tabModel:self didChangeTabSnapshot:tab withImage:image];
513}
514
sdefresne67dfdd62016-12-19 12:43:12515- (void)setWebUsageEnabled:(BOOL)webUsageEnabled {
sdefresnefd2aa652017-05-23 14:23:10516 if (_webUsageEnabled == webUsageEnabled)
sdefresne67dfdd62016-12-19 12:43:12517 return;
sdefresnefd2aa652017-05-23 14:23:10518 _webUsageEnabled = webUsageEnabled;
sdefresne7d699dd2017-04-05 13:05:23519 for (int index = 0; index < _webStateList->count(); ++index) {
520 web::WebState* webState = _webStateList->GetWebStateAt(index);
sdefresnefd2aa652017-05-23 14:23:10521 webState->SetWebUsageEnabled(_webUsageEnabled ? true : false);
sdefresne67dfdd62016-12-19 12:43:12522 }
523}
524
525- (void)setPrimary:(BOOL)primary {
Sylvain Defresne890975f52017-08-24 17:42:26526 if (_tabUsageRecorder) {
527 _tabUsageRecorder->RecordPrimaryTabModelChange(primary,
528 self.currentTab.webState);
529 }
sdefresne67dfdd62016-12-19 12:43:12530}
531
532- (NSSet*)currentlyReferencedExternalFiles {
533 NSMutableSet* referencedFiles = [NSMutableSet set];
534 if (!_browserState)
535 return referencedFiles;
536 // Check the currently open tabs for external files.
sdefresne32c0c382017-02-17 16:37:26537 for (Tab* tab in self) {
kkhorimoto2a192b82017-06-01 20:11:08538 const GURL& lastCommittedURL = tab.lastCommittedURL;
539 if (UrlIsExternalFileReference(lastCommittedURL)) {
540 [referencedFiles addObject:base::SysUTF8ToNSString(
541 lastCommittedURL.ExtractFileName())];
542 }
543 web::NavigationItem* pendingItem =
544 tab.webState->GetNavigationManager()->GetPendingItem();
545 if (pendingItem && UrlIsExternalFileReference(pendingItem->GetURL())) {
546 [referencedFiles addObject:base::SysUTF8ToNSString(
547 pendingItem->GetURL().ExtractFileName())];
sdefresne67dfdd62016-12-19 12:43:12548 }
549 }
550 // Do the same for the recently closed tabs.
551 sessions::TabRestoreService* restoreService =
552 IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState);
553 DCHECK(restoreService);
554 for (const auto& entry : restoreService->entries()) {
555 sessions::TabRestoreService::Tab* tab =
556 static_cast<sessions::TabRestoreService::Tab*>(entry.get());
557 int navigationIndex = tab->current_navigation_index;
558 sessions::SerializedNavigationEntry navigation =
559 tab->navigations[navigationIndex];
560 GURL URL = navigation.virtual_url();
561 if (UrlIsExternalFileReference(URL)) {
562 NSString* fileName = base::SysUTF8ToNSString(URL.ExtractFileName());
563 [referencedFiles addObject:fileName];
564 }
565 }
566 return referencedFiles;
567}
568
569// NOTE: This can be called multiple times, so must be robust against that.
570- (void)browserStateDestroyed {
sdefresne05315352017-05-23 14:46:17571 if (!_browserState)
572 return;
573
sdefresne67dfdd62016-12-19 12:43:12574 [[NSNotificationCenter defaultCenter] removeObserver:self];
sdefresne05315352017-05-23 14:46:17575 UnregisterTabModelFromChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12576 _browserState = nullptr;
sdefresne05315352017-05-23 14:46:17577
578 // Clear weak pointer to WebStateListMetricsObserver before destroying it.
579 _webStateListMetricsObserver = nullptr;
580
581 // Close all tabs. Do this in an @autoreleasepool as WebStateList observers
582 // will be notified (they are unregistered later). As some of them may be
583 // implemented in Objective-C and unregister themselves in their -dealloc
584 // method, ensure they -autorelease introduced by ARC are processed before
585 // the WebStateList destructor is called.
586 @autoreleasepool {
587 [self closeAllTabs];
588 }
589
590 // Unregister all observers after closing all the tabs as some of them are
591 // required to properly clean up the Tabs.
592 for (const auto& webStateListObserver : _webStateListObservers)
593 _webStateList->RemoveObserver(webStateListObserver.get());
594 _webStateListObservers.clear();
595 _retainedWebStateListObservers = nil;
596
597 _clearPoliciesTaskTracker.TryCancelAll();
Sylvain Defresne890975f52017-08-24 17:42:26598 _tabUsageRecorder.reset();
sdefresne67dfdd62016-12-19 12:43:12599}
600
sdefresne573bd7b2017-03-03 00:12:49601- (void)navigationCommittedInTab:(Tab*)tab
602 previousItem:(web::NavigationItem*)previousItem {
sdefresne67dfdd62016-12-19 12:43:12603 if (self.offTheRecord)
604 return;
605 if (![tab navigationManager])
606 return;
607
608 // See if the navigation was within a page; if so ignore it.
sdefresne67dfdd62016-12-19 12:43:12609 if (previousItem) {
610 GURL previousURL = previousItem->GetURL();
611 GURL currentURL = [tab navigationManager]->GetVisibleItem()->GetURL();
612
613 url::Replacements<char> replacements;
614 replacements.ClearRef();
615 if (previousURL.ReplaceComponents(replacements) ==
616 currentURL.ReplaceComponents(replacements)) {
617 return;
618 }
619 }
620
621 int tabCount = static_cast<int>(self.count);
622 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tabCount, 1, 200, 50);
623}
624
sdefresne297553e52017-01-25 17:09:37625#pragma mark - NSFastEnumeration
626
627- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)state
sdefresne4162a4dc2017-05-15 11:26:24628 objects:(id __unsafe_unretained*)objects
sdefresne297553e52017-01-25 17:09:37629 count:(NSUInteger)count {
sdefresne4a0a4e852017-04-19 13:16:14630 return [_fastEnumerationHelper->GetFastEnumeration()
631 countByEnumeratingWithState:state
632 objects:objects
633 count:count];
sdefresne297553e52017-01-25 17:09:37634}
635
sdefresne67dfdd62016-12-19 12:43:12636#pragma mark - Private methods
637
sdefresnedcc6c6e2017-04-19 15:49:02638- (SessionIOS*)sessionForSaving {
sdefresne67dfdd62016-12-19 12:43:12639 // Build the array of sessions. Copy the session objects as the saving will
640 // be done on a separate thread.
641 // TODO(crbug.com/661986): This could get expensive especially since this
642 // window may never be saved (if another call comes in before the delay).
sdefresne68f546a2017-04-20 10:54:03643 return [[SessionIOS alloc]
644 initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]];
sdefresne67dfdd62016-12-19 12:43:12645}
646
sdefresne44c09dc2017-02-07 11:13:08647- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window
648 persistState:(BOOL)persistState {
649 DCHECK(_browserState);
650 DCHECK(window);
651
sdefresne68f546a2017-04-20 10:54:03652 if (!window.sessions.count)
sdefresne44c09dc2017-02-07 11:13:08653 return NO;
654
sdefresne365a73a2017-03-14 15:07:13655 int oldCount = _webStateList->count();
sdefresne32c0c382017-02-17 16:37:26656 DCHECK_GE(oldCount, 0);
657
sdefresne08c0ab42017-04-05 12:49:43658 web::WebState::CreateParams createParams(_browserState);
659 DeserializeWebStateList(
sdefresnefd2aa652017-05-23 14:23:10660 _webStateList.get(), window,
Sylvain Defresne2b834542017-08-10 14:50:02661 base::BindRepeating(&web::WebState::CreateWithStorageSession,
662 createParams));
sdefresne08c0ab42017-04-05 12:49:43663
664 DCHECK_GT(_webStateList->count(), oldCount);
665 int restoredCount = _webStateList->count() - oldCount;
sdefresne68f546a2017-04-20 10:54:03666 DCHECK_EQ(window.sessions.count, static_cast<NSUInteger>(restoredCount));
sdefresne08c0ab42017-04-05 12:49:43667
sdefresnec92bada2017-02-08 14:43:49668 scoped_refptr<web::CertificatePolicyCache> policyCache =
669 web::BrowserState::GetCertificatePolicyCache(_browserState);
sdefresne44c09dc2017-02-07 11:13:08670
Sylvain Defresne890975f52017-08-24 17:42:26671 std::vector<web::WebState*> restoredWebStates;
672 if (_tabUsageRecorder)
673 restoredWebStates.reserve(window.sessions.count);
sdefresnee0a9ba62017-02-23 09:14:11674
sdefresne2c600c52017-04-04 16:49:59675 for (int index = oldCount; index < _webStateList->count(); ++index) {
676 web::WebState* webState = _webStateList->GetWebStateAt(index);
Eugene Butb56ecad2017-08-10 21:09:52677 PagePlaceholderTabHelper::FromWebState(webState)
678 ->AddPlaceholderForNextNavigation();
sdefresne44c09dc2017-02-07 11:13:08679
680 // Restore the CertificatePolicyCache (note that webState is invalid after
sdefresnebf75f022017-03-07 17:48:35681 // passing it via move semantic to -initWithWebState:model:).
Eugene Butb56ecad2017-08-10 21:09:52682 UpdateCertificatePolicyCacheFromWebState(policyCache, webState);
Sylvain Defresne890975f52017-08-24 17:42:26683
684 if (_tabUsageRecorder)
685 restoredWebStates.push_back(webState);
sdefresne44c09dc2017-02-07 11:13:08686 }
sdefresnee0a9ba62017-02-23 09:14:11687
sdefresne44c09dc2017-02-07 11:13:08688 // If there was only one tab and it was the new tab page, clobber it.
689 BOOL closedNTPTab = NO;
690 if (oldCount == 1) {
sdefresne32c0c382017-02-17 16:37:26691 Tab* tab = [self tabAtIndex:0];
kkhorimoto2a192b82017-06-01 20:11:08692 BOOL hasPendingLoad =
693 tab.webState->GetNavigationManager()->GetPendingItem() != nullptr;
694 if (!hasPendingLoad && tab.lastCommittedURL == GURL(kChromeUINewTabURL)) {
sdefresne44c09dc2017-02-07 11:13:08695 [self closeTab:tab];
696 closedNTPTab = YES;
697 oldCount = 0;
698 }
699 }
Sylvain Defresne890975f52017-08-24 17:42:26700 if (_tabUsageRecorder) {
701 _tabUsageRecorder->InitialRestoredTabs(self.currentTab.webState,
702 restoredWebStates);
703 }
sdefresne44c09dc2017-02-07 11:13:08704 return closedNTPTab;
705}
706
sdefresne67dfdd62016-12-19 12:43:12707#pragma mark - Notification Handlers
708
709// Called when UIApplicationWillResignActiveNotification is received.
710- (void)willResignActive:(NSNotification*)notify {
sdefresnefd2aa652017-05-23 14:23:10711 if (_webUsageEnabled && self.currentTab) {
Sylvain Defresne528c17b2017-06-08 15:00:21712 [SnapshotCacheFactory::GetForBrowserState(_browserState)
sdefresne2f7781c2017-03-02 19:12:46713 willBeSavedGreyWhenBackgrounding:self.currentTab.tabId];
sdefresne67dfdd62016-12-19 12:43:12714 }
715}
716
717// Called when UIApplicationDidEnterBackgroundNotification is received.
718- (void)applicationDidEnterBackground:(NSNotification*)notify {
719 if (!_browserState)
720 return;
sdefresnec92bada2017-02-08 14:43:49721
sdefresne67dfdd62016-12-19 12:43:12722 // Evict all the certificate policies except for the current entries of the
723 // active sessions.
sdefresnec92bada2017-02-08 14:43:49724 CleanCertificatePolicyCache(
725 &_clearPoliciesTaskTracker,
726 web::WebThread::GetTaskRunnerForThread(web::WebThread::IO),
sdefresne32c0c382017-02-17 16:37:26727 web::BrowserState::GetCertificatePolicyCache(_browserState),
sdefresne365a73a2017-03-14 15:07:13728 _webStateList.get());
sdefresne67dfdd62016-12-19 12:43:12729
730 if (_tabUsageRecorder)
731 _tabUsageRecorder->AppDidEnterBackground();
732
733 // Normally, the session is saved after some timer expires but since the app
734 // is about to enter the background send YES to save the session immediately.
735 [self saveSessionImmediately:YES];
736
737 // Write out a grey version of the current website to disk.
sdefresnefd2aa652017-05-23 14:23:10738 if (_webUsageEnabled && self.currentTab) {
Sylvain Defresne528c17b2017-06-08 15:00:21739 [SnapshotCacheFactory::GetForBrowserState(_browserState)
sdefresne2f7781c2017-03-02 19:12:46740 saveGreyInBackgroundForSessionID:self.currentTab.tabId];
sdefresne67dfdd62016-12-19 12:43:12741 }
742}
743
744// Called when UIApplicationWillEnterForegroundNotification is received.
745- (void)applicationWillEnterForeground:(NSNotification*)notify {
746 if (_tabUsageRecorder) {
747 _tabUsageRecorder->AppWillEnterForeground();
748 }
749}
750
751@end