blob: 8ffe50185d6d2b53ca61fdc2b10252918976054b [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"
17#include "base/strings/sys_string_conversions.h"
sdefresnec92bada2017-02-08 14:43:4918#include "base/task/cancelable_task_tracker.h"
Sylvain Defresne7178d4c2017-09-14 13:22:3719#include "components/favicon/ios/web_favicon_driver.h"
Sylvain Defresne94394332018-01-22 13:11:0020#include "components/navigation_metrics/navigation_metrics.h"
sdefresne67dfdd62016-12-19 12:43:1221#include "components/sessions/core/serialized_navigation_entry.h"
22#include "components/sessions/core/session_id.h"
23#include "components/sessions/core/tab_restore_service.h"
24#include "components/sessions/ios/ios_live_tab.h"
25#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
26#include "ios/chrome/browser/chrome_url_constants.h"
27#import "ios/chrome/browser/chrome_url_util.h"
Sylvain Defresne94394332018-01-22 13:11:0028#include "ios/chrome/browser/crash_loop_detection_util.h"
29#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
sdefresne67dfdd62016-12-19 12:43:1230#import "ios/chrome/browser/metrics/tab_usage_recorder.h"
Sylvain Defresne890975f52017-08-24 17:42:2631#import "ios/chrome/browser/prerender/prerender_service_factory.h"
sdefresne67dfdd62016-12-19 12:43:1232#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
sdefresnedcc6c6e2017-04-19 15:49:0233#import "ios/chrome/browser/sessions/session_ios.h"
sdefresneda974c82017-04-06 17:24:3934#import "ios/chrome/browser/sessions/session_service_ios.h"
sdefresne56528152017-03-22 17:03:2835#import "ios/chrome/browser/sessions/session_window_ios.h"
sdefresne67dfdd62016-12-19 12:43:1236#import "ios/chrome/browser/snapshots/snapshot_cache.h"
Sylvain Defresne528c17b2017-06-08 15:00:2137#import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
sdefresne2f7781c2017-03-02 19:12:4638#import "ios/chrome/browser/snapshots/snapshot_cache_web_state_list_observer.h"
sdefresne67dfdd62016-12-19 12:43:1239#include "ios/chrome/browser/tab_parenting_global_observer.h"
sdefresne32c0c382017-02-17 16:37:2640#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
sdefresne67dfdd62016-12-19 12:43:1241#import "ios/chrome/browser/tabs/tab.h"
sdefresne2c600c52017-04-04 16:49:5942#import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h"
Sylvain Defresne17b7b33f2017-11-10 19:55:1543#import "ios/chrome/browser/tabs/tab_model_favicon_driver_observer.h"
sdefresne8cfdf8f2017-01-11 18:22:1444#import "ios/chrome/browser/tabs/tab_model_list.h"
Sylvain Defresne2b834542017-08-10 14:50:0245#import "ios/chrome/browser/tabs/tab_model_notification_observer.h"
sdefresne0a762e42017-02-14 11:11:2546#import "ios/chrome/browser/tabs/tab_model_observers.h"
sdefresne39bfaab2017-02-20 11:06:3647#import "ios/chrome/browser/tabs/tab_model_observers_bridge.h"
sdefresne2f7781c2017-03-02 19:12:4648#import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h"
sdefresne67dfdd62016-12-19 12:43:1249#import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h"
sdefresne365a73a2017-03-14 15:07:1350#import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.h"
Sylvain Defresnee8ae6ad2017-09-14 14:22:1051#import "ios/chrome/browser/tabs/tab_model_web_usage_enabled_observer.h"
sdefresne39bfaab2017-02-20 11:06:3652#import "ios/chrome/browser/tabs/tab_parenting_observer.h"
Eugene Butb56ecad2017-08-10 21:09:5253#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
sdefresne62a00bb2017-04-10 15:36:0554#import "ios/chrome/browser/web_state_list/web_state_list.h"
sdefresne62a00bb2017-04-10 15:36:0555#import "ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h"
56#import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
57#import "ios/chrome/browser/web_state_list/web_state_list_serialization.h"
58#import "ios/chrome/browser/web_state_list/web_state_opener.h"
sdefresne67dfdd62016-12-19 12:43:1259#include "ios/web/public/browser_state.h"
60#include "ios/web/public/certificate_policy_cache.h"
Sylvain Defresne94394332018-01-22 13:11:0061#import "ios/web/public/load_committed_details.h"
sdefresne67dfdd62016-12-19 12:43:1262#include "ios/web/public/navigation_item.h"
63#import "ios/web/public/navigation_manager.h"
sdefresne2c600c52017-04-04 16:49:5964#import "ios/web/public/serializable_user_data_manager.h"
Sylvain Defresne94394332018-01-22 13:11:0065#import "ios/web/public/web_state/navigation_context.h"
kkhorimotod3a3986f2017-04-05 02:21:4166#include "ios/web/public/web_state/session_certificate_policy_cache.h"
Sylvain Defresne94394332018-01-22 13:11:0067#import "ios/web/public/web_state/web_state_observer_bridge.h"
sdefresne67dfdd62016-12-19 12:43:1268#include "ios/web/public/web_thread.h"
sdefresne67dfdd62016-12-19 12:43:1269#include "url/gurl.h"
70
sdefresne4162a4dc2017-05-15 11:26:2471#if !defined(__has_feature) || !__has_feature(objc_arc)
72#error "This file requires ARC support."
73#endif
74
sdefresne67dfdd62016-12-19 12:43:1275namespace {
76
77// Updates CRWSessionCertificatePolicyManager's certificate policy cache.
sdefresnec92bada2017-02-08 14:43:4978void UpdateCertificatePolicyCacheFromWebState(
79 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
kkhorimotod3a3986f2017-04-05 02:21:4180 const web::WebState* web_state) {
sdefresne4c8bf632017-01-26 15:46:4781 DCHECK(web_state);
sdefresnec92bada2017-02-08 14:43:4982 DCHECK_CURRENTLY_ON(web::WebThread::UI);
kkhorimotod3a3986f2017-04-05 02:21:4183 web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache(
84 policy_cache);
sdefresne67dfdd62016-12-19 12:43:1285}
86
sdefresne32c0c382017-02-17 16:37:2687// Populates the certificate policy cache based on the WebStates of
88// |web_state_list|.
sdefresnec92bada2017-02-08 14:43:4989void RestoreCertificatePolicyCacheFromModel(
90 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:2691 WebStateList* web_state_list) {
sdefresnec92bada2017-02-08 14:43:4992 DCHECK_CURRENTLY_ON(web::WebThread::UI);
sdefresne32c0c382017-02-17 16:37:2693 for (int index = 0; index < web_state_list->count(); ++index) {
94 UpdateCertificatePolicyCacheFromWebState(
95 policy_cache, web_state_list->GetWebStateAt(index));
96 }
sdefresne67dfdd62016-12-19 12:43:1297}
98
sdefresnec92bada2017-02-08 14:43:4999// Scrubs the certificate policy cache of all certificates policies except
sdefresne32c0c382017-02-17 16:37:26100// those for the current entries in |web_state_list|.
sdefresne67dfdd62016-12-19 12:43:12101void CleanCertificatePolicyCache(
sdefresnec92bada2017-02-08 14:43:49102 base::CancelableTaskTracker* task_tracker,
103 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
104 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:26105 WebStateList* web_state_list) {
sdefresne67dfdd62016-12-19 12:43:12106 DCHECK(policy_cache);
sdefresne32c0c382017-02-17 16:37:26107 DCHECK(web_state_list);
sdefresnec92bada2017-02-08 14:43:49108 DCHECK_CURRENTLY_ON(web::WebThread::UI);
109 task_tracker->PostTaskAndReply(
110 task_runner.get(), FROM_HERE,
111 base::Bind(&web::CertificatePolicyCache::ClearCertificatePolicies,
112 policy_cache),
113 base::Bind(&RestoreCertificatePolicyCacheFromModel, policy_cache,
sdefresne32c0c382017-02-17 16:37:26114 base::Unretained(web_state_list)));
sdefresne67dfdd62016-12-19 12:43:12115}
116
Sylvain Defresne94394332018-01-22 13:11:00117// Returns whether |rhs| and |lhs| are different user agent types. If either
118// of them is web::UserAgentType::NONE, then return NO.
119BOOL IsTransitionBetweenDesktopAndMobileUserAgent(web::UserAgentType lhs,
120 web::UserAgentType rhs) {
121 if (lhs == web::UserAgentType::NONE)
122 return NO;
123
124 if (rhs == web::UserAgentType::NONE)
125 return NO;
126
127 return lhs != rhs;
128}
129
130// Returns whether TabUsageRecorder::RecordPageLoadStart should be called for
131// the given navigation.
132BOOL ShouldRecordPageLoadStartForNavigation(
133 web::NavigationContext* navigation) {
134 web::NavigationManager* navigation_manager =
135 navigation->GetWebState()->GetNavigationManager();
136
137 web::NavigationItem* last_committed_item =
138 navigation_manager->GetLastCommittedItem();
139 if (!last_committed_item) {
140 // Opening a child window and loading URL there.
141 // http://crbug.com/773160
142 return NO;
143 }
144
145 web::NavigationItem* pending_item = navigation_manager->GetPendingItem();
146 if (pending_item) {
147 if (IsTransitionBetweenDesktopAndMobileUserAgent(
148 pending_item->GetUserAgentType(),
149 last_committed_item->GetUserAgentType())) {
150 // Switching between Desktop and Mobile user agent.
151 return NO;
152 }
153 }
154
155 ui::PageTransition transition = navigation->GetPageTransition();
156 if (!ui::PageTransitionIsNewNavigation(transition)) {
157 // Back/forward navigation or reload.
158 return NO;
159 }
160
161 if ((transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0) {
162 // Client redirect.
163 return NO;
164 }
165
166 static const ui::PageTransition kRecordedPageTransitionTypes[] = {
167 ui::PAGE_TRANSITION_TYPED,
168 ui::PAGE_TRANSITION_LINK,
169 ui::PAGE_TRANSITION_GENERATED,
170 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
171 ui::PAGE_TRANSITION_FORM_SUBMIT,
172 ui::PAGE_TRANSITION_KEYWORD,
173 ui::PAGE_TRANSITION_KEYWORD_GENERATED,
174 };
175
176 for (size_t i = 0; i < arraysize(kRecordedPageTransitionTypes); ++i) {
177 const ui::PageTransition recorded_type = kRecordedPageTransitionTypes[i];
178 if (ui::PageTransitionCoreTypeIs(transition, recorded_type)) {
179 return YES;
180 }
181 }
182
183 return NO;
184}
185
186// Records metrics for the interface's orientation.
187void RecordInterfaceOrientationMetric() {
188 switch ([[UIApplication sharedApplication] statusBarOrientation]) {
189 case UIInterfaceOrientationPortrait:
190 case UIInterfaceOrientationPortraitUpsideDown:
191 UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", YES);
192 break;
193 case UIInterfaceOrientationLandscapeLeft:
194 case UIInterfaceOrientationLandscapeRight:
195 UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", NO);
196 break;
197 case UIInterfaceOrientationUnknown:
198 // TODO(crbug.com/228832): Convert from a boolean histogram to an
199 // enumerated histogram and log this case as well.
200 break;
201 }
202}
203
204// Records metrics for main frame navigation.
205void RecordMainFrameNavigationMetric(web::WebState* web_state) {
206 DCHECK(web_state);
207 DCHECK(web_state->GetBrowserState());
208 DCHECK(web_state->GetNavigationManager());
209 web::NavigationItem* item =
210 web_state->GetNavigationManager()->GetLastCommittedItem();
211 navigation_metrics::RecordMainFrameNavigation(
212 item ? item->GetVirtualURL() : GURL::EmptyGURL(), true,
213 web_state->GetBrowserState()->IsOffTheRecord());
214}
215
sdefresne67dfdd62016-12-19 12:43:12216} // anonymous namespace
217
Sylvain Defresne94394332018-01-22 13:11:00218@interface TabModel ()<CRWWebStateObserver, WebStateListObserving> {
sdefresne365a73a2017-03-14 15:07:13219 // Delegate for the WebStateList.
220 std::unique_ptr<WebStateListDelegate> _webStateListDelegate;
221
sdefresne32c0c382017-02-17 16:37:26222 // Underlying shared model implementation.
sdefresne365a73a2017-03-14 15:07:13223 std::unique_ptr<WebStateList> _webStateList;
sdefresne32c0c382017-02-17 16:37:26224
sdefresneee210332017-03-24 10:36:54225 // WebStateListObservers reacting to modifications of the model (may send
226 // notification, translate and forward events, update metrics, ...).
227 std::vector<std::unique_ptr<WebStateListObserver>> _webStateListObservers;
sdefresne39bfaab2017-02-20 11:06:36228
sdefresne3b0155e92017-04-12 15:18:53229 // Strong references to id<WebStateListObserving> wrapped by non-owning
230 // WebStateListObserverBridges.
sdefresne4162a4dc2017-05-15 11:26:24231 NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers;
sdefresne3b0155e92017-04-12 15:18:53232
sdefresne67dfdd62016-12-19 12:43:12233 // The delegate for sync.
234 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate;
sdefresne67dfdd62016-12-19 12:43:12235
Mark Cogan5b494642018-01-02 12:55:46236 // The observer that calls -notifyNewTabWillOpen on this object.
Sylvain Defresnee8ae6ad2017-09-14 14:22:10237 TabModelNotificationObserver* _tabModelNotificationObserver;
238
sdefresne67dfdd62016-12-19 12:43:12239 // Counters for metrics.
sdefresne2f7781c2017-03-02 19:12:46240 WebStateListMetricsObserver* _webStateListMetricsObserver;
sdefresne67dfdd62016-12-19 12:43:12241
242 // Backs up property with the same name.
243 std::unique_ptr<TabUsageRecorder> _tabUsageRecorder;
sdefresne67dfdd62016-12-19 12:43:12244 // Saves session's state.
sdefresne4162a4dc2017-05-15 11:26:24245 SessionServiceIOS* _sessionService;
sdefresne67dfdd62016-12-19 12:43:12246 // List of TabModelObservers.
sdefresne4162a4dc2017-05-15 11:26:24247 TabModelObservers* _observers;
sdefresnec92bada2017-02-08 14:43:49248
249 // Used to ensure thread-safety of the certificate policy management code.
250 base::CancelableTaskTracker _clearPoliciesTaskTracker;
Sylvain Defresne94394332018-01-22 13:11:00251
252 // Used to observe owned Tabs' WebStates.
253 std::unique_ptr<web::WebStateObserver> _webStateObserver;
sdefresne67dfdd62016-12-19 12:43:12254}
255
256// Session window for the contents of the tab model.
sdefresnedcc6c6e2017-04-19 15:49:02257@property(nonatomic, readonly) SessionIOS* sessionForSaving;
sdefresne67dfdd62016-12-19 12:43:12258
sdefresne44c09dc2017-02-07 11:13:08259// Helper method to restore a saved session and control if the state should
260// be persisted or not. Used to implement the public -restoreSessionWindow:
261// method and restoring session in the initialiser.
262- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window
263 persistState:(BOOL)persistState;
264
sdefresne67dfdd62016-12-19 12:43:12265@end
266
267@implementation TabModel
268
269@synthesize browserState = _browserState;
sdefresnefd2aa652017-05-23 14:23:10270@synthesize webUsageEnabled = _webUsageEnabled;
sdefresne67dfdd62016-12-19 12:43:12271
272#pragma mark - Overriden
273
274- (void)dealloc {
sdefresne67dfdd62016-12-19 12:43:12275 // browserStateDestroyed should always have been called before destruction.
276 DCHECK(!_browserState);
277
sdefresne05315352017-05-23 14:46:17278 // Make sure the observers do clean after themselves.
279 DCHECK([_observers empty]);
sdefresne67dfdd62016-12-19 12:43:12280}
281
282#pragma mark - Public methods
283
284- (Tab*)currentTab {
sdefresne365a73a2017-03-14 15:07:13285 web::WebState* webState = _webStateList->GetActiveWebState();
sdefresne2f7781c2017-03-02 19:12:46286 return webState ? LegacyTabHelper::GetTabForWebState(webState) : nil;
sdefresne67dfdd62016-12-19 12:43:12287}
288
289- (void)setCurrentTab:(Tab*)newTab {
sdefresne365a73a2017-03-14 15:07:13290 int indexOfTab = _webStateList->GetIndexOfWebState(newTab.webState);
sdefresne2f7781c2017-03-02 19:12:46291 DCHECK_NE(indexOfTab, WebStateList::kInvalidIndex);
sdefresne365a73a2017-03-14 15:07:13292 _webStateList->ActivateWebStateAt(indexOfTab);
sdefresne67dfdd62016-12-19 12:43:12293}
294
295- (TabModelSyncedWindowDelegate*)syncedWindowDelegate {
296 return _syncedWindowDelegate.get();
297}
298
299- (TabUsageRecorder*)tabUsageRecorder {
300 return _tabUsageRecorder.get();
301}
302
303- (BOOL)isOffTheRecord {
304 return _browserState && _browserState->IsOffTheRecord();
305}
306
307- (BOOL)isEmpty {
sdefresne365a73a2017-03-14 15:07:13308 return _webStateList->empty();
sdefresne67dfdd62016-12-19 12:43:12309}
310
sdefresne297553e52017-01-25 17:09:37311- (NSUInteger)count {
sdefresne365a73a2017-03-14 15:07:13312 DCHECK_GE(_webStateList->count(), 0);
313 return static_cast<NSUInteger>(_webStateList->count());
sdefresne297553e52017-01-25 17:09:37314}
315
sdefresneee210332017-03-24 10:36:54316- (WebStateList*)webStateList {
317 return _webStateList.get();
318}
319
sdefresne67dfdd62016-12-19 12:43:12320- (instancetype)initWithSessionWindow:(SessionWindowIOS*)window
321 sessionService:(SessionServiceIOS*)service
322 browserState:(ios::ChromeBrowserState*)browserState {
323 if ((self = [super init])) {
sdefresne4162a4dc2017-05-15 11:26:24324 _observers = [TabModelObservers observers];
sdefresne67dfdd62016-12-19 12:43:12325
sdefresne365a73a2017-03-14 15:07:13326 _webStateListDelegate =
Jinho Bangb940d6f42018-01-18 22:39:04327 std::make_unique<TabModelWebStateListDelegate>(self);
328 _webStateList = std::make_unique<WebStateList>(_webStateListDelegate.get());
sdefresne365a73a2017-03-14 15:07:13329
sdefresne67dfdd62016-12-19 12:43:12330 _browserState = browserState;
331 DCHECK(_browserState);
332
Sylvain Defresne94394332018-01-22 13:11:00333 _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
334
sdefresne67dfdd62016-12-19 12:43:12335 // Normal browser states are the only ones to get tab restore. Tab sync
336 // handles incognito browser states by filtering on profile, so it's
337 // important to the backend code to always have a sync window delegate.
338 if (!_browserState->IsOffTheRecord()) {
339 // Set up the usage recorder before tabs are created.
Jinho Bangb940d6f42018-01-18 22:39:04340 _tabUsageRecorder = std::make_unique<TabUsageRecorder>(
Sylvain Defresne890975f52017-08-24 17:42:26341 _webStateList.get(),
342 PrerenderServiceFactory::GetForBrowserState(browserState));
sdefresne67dfdd62016-12-19 12:43:12343 }
Sylvain Defresne72c1e992018-01-15 14:26:17344 _syncedWindowDelegate =
Jinho Bangb940d6f42018-01-18 22:39:04345 std::make_unique<TabModelSyncedWindowDelegate>(_webStateList.get());
sdefresne41971892017-02-23 17:24:14346
347 // There must be a valid session service defined to consume session windows.
348 DCHECK(service);
sdefresne4162a4dc2017-05-15 11:26:24349 _sessionService = service;
sdefresne41971892017-02-23 17:24:14350
sdefresne4162a4dc2017-05-15 11:26:24351 NSMutableArray<id<WebStateListObserving>>* retainedWebStateListObservers =
352 [[NSMutableArray alloc] init];
sdefresne3b0155e92017-04-12 15:18:53353
sdefresne4162a4dc2017-05-15 11:26:24354 TabModelClosingWebStateObserver* tabModelClosingWebStateObserver = [
355 [TabModelClosingWebStateObserver alloc]
356 initWithTabModel:self
357 restoreService:IOSChromeTabRestoreServiceFactory::GetForBrowserState(
358 _browserState)];
sdefresne3b0155e92017-04-12 15:18:53359 [retainedWebStateListObservers addObject:tabModelClosingWebStateObserver];
360
361 _webStateListObservers.push_back(
Sylvain Defresne94394332018-01-22 13:11:00362 std::make_unique<WebStateListObserverBridge>(self));
363
364 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04365 std::make_unique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53366 tabModelClosingWebStateObserver));
367
Sylvain Defresne528c17b2017-06-08 15:00:21368 SnapshotCache* snapshotCache =
369 SnapshotCacheFactory::GetForBrowserState(_browserState);
370 if (snapshotCache) {
371 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04372 std::make_unique<SnapshotCacheWebStateListObserver>(snapshotCache));
Sylvain Defresne528c17b2017-06-08 15:00:21373 }
374
Jinho Bangb940d6f42018-01-18 22:39:04375 _webStateListObservers.push_back(std::make_unique<TabParentingObserver>());
sdefresne3b0155e92017-04-12 15:18:53376
sdefresne4162a4dc2017-05-15 11:26:24377 TabModelSelectedTabObserver* tabModelSelectedTabObserver =
378 [[TabModelSelectedTabObserver alloc] initWithTabModel:self];
sdefresne3b0155e92017-04-12 15:18:53379 [retainedWebStateListObservers addObject:tabModelSelectedTabObserver];
sdefresneee210332017-03-24 10:36:54380 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04381 std::make_unique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53382 tabModelSelectedTabObserver));
383
sdefresne4162a4dc2017-05-15 11:26:24384 TabModelObserversBridge* tabModelObserversBridge =
sdefresne3b0155e92017-04-12 15:18:53385 [[TabModelObserversBridge alloc] initWithTabModel:self
sdefresne4162a4dc2017-05-15 11:26:24386 tabModelObservers:_observers];
sdefresne3b0155e92017-04-12 15:18:53387 [retainedWebStateListObservers addObject:tabModelObserversBridge];
sdefresneee210332017-03-24 10:36:54388 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04389 std::make_unique<WebStateListObserverBridge>(tabModelObserversBridge));
sdefresne2f7781c2017-03-02 19:12:46390
Sylvain Defresne17b7b33f2017-11-10 19:55:15391 _webStateListObservers.push_back(
392 std::make_unique<TabModelFaviconDriverObserver>(self, _observers));
393
sdefresne2f7781c2017-03-02 19:12:46394 auto webStateListMetricsObserver =
Jinho Bangb940d6f42018-01-18 22:39:04395 std::make_unique<WebStateListMetricsObserver>();
sdefresne2f7781c2017-03-02 19:12:46396 _webStateListMetricsObserver = webStateListMetricsObserver.get();
sdefresneee210332017-03-24 10:36:54397 _webStateListObservers.push_back(std::move(webStateListMetricsObserver));
sdefresne41971892017-02-23 17:24:14398
Sylvain Defresne2b834542017-08-10 14:50:02399 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04400 std::make_unique<TabModelWebUsageEnabledObserver>(self));
Sylvain Defresnee8ae6ad2017-09-14 14:22:10401
402 auto tabModelNotificationObserver =
Jinho Bangb940d6f42018-01-18 22:39:04403 std::make_unique<TabModelNotificationObserver>(self);
Sylvain Defresnee8ae6ad2017-09-14 14:22:10404 _tabModelNotificationObserver = tabModelNotificationObserver.get();
405 _webStateListObservers.push_back(std::move(tabModelNotificationObserver));
Sylvain Defresne2b834542017-08-10 14:50:02406
sdefresneee210332017-03-24 10:36:54407 for (const auto& webStateListObserver : _webStateListObservers)
408 _webStateList->AddObserver(webStateListObserver.get());
sdefresne4162a4dc2017-05-15 11:26:24409 _retainedWebStateListObservers = [retainedWebStateListObservers copy];
sdefresne67dfdd62016-12-19 12:43:12410
sdefresne67dfdd62016-12-19 12:43:12411 if (window) {
sdefresne44c09dc2017-02-07 11:13:08412 DCHECK([_observers empty]);
413 // Restore the session and reset the session metrics (as the event have
414 // not been generated by the user but by a cold start cycle).
415 [self restoreSessionWindow:window persistState:NO];
416 [self resetSessionMetrics];
sdefresne67dfdd62016-12-19 12:43:12417 }
418
sdefresne67dfdd62016-12-19 12:43:12419 // Register for resign active notification.
sdefresne44c09dc2017-02-07 11:13:08420 [[NSNotificationCenter defaultCenter]
421 addObserver:self
422 selector:@selector(willResignActive:)
423 name:UIApplicationWillResignActiveNotification
424 object:nil];
sdefresne67dfdd62016-12-19 12:43:12425 // Register for background notification.
sdefresne44c09dc2017-02-07 11:13:08426 [[NSNotificationCenter defaultCenter]
427 addObserver:self
428 selector:@selector(applicationDidEnterBackground:)
429 name:UIApplicationDidEnterBackgroundNotification
430 object:nil];
sdefresne8cfdf8f2017-01-11 18:22:14431
432 // Associate with ios::ChromeBrowserState.
Mohamad Ahmadie04d0952017-11-17 23:31:24433 TabModelList::RegisterTabModelWithChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12434 }
435 return self;
436}
437
438- (instancetype)init {
439 NOTREACHED();
440 return nil;
441}
442
443- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window {
sdefresne44c09dc2017-02-07 11:13:08444 return [self restoreSessionWindow:window persistState:YES];
sdefresne67dfdd62016-12-19 12:43:12445}
446
447- (void)saveSessionImmediately:(BOOL)immediately {
448 // Do nothing if there are tabs in the model but no selected tab. This is
449 // a transitional state.
sdefresne365a73a2017-03-14 15:07:13450 if ((!self.currentTab && _webStateList->count()) || !_browserState)
sdefresne67dfdd62016-12-19 12:43:12451 return;
sdefresne80a544622017-04-12 12:44:27452 NSString* statePath =
453 base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe());
Sylvain Defresne5fbebad2017-06-01 11:23:53454 __weak TabModel* weakSelf = self;
455 SessionIOSFactory sessionFactory = ^{
456 return weakSelf.sessionForSaving;
457 };
458 [_sessionService saveSession:sessionFactory
sdefresnedcc6c6e2017-04-19 15:49:02459 directory:statePath
460 immediately:immediately];
sdefresne67dfdd62016-12-19 12:43:12461}
462
463- (Tab*)tabAtIndex:(NSUInteger)index {
sdefresne32c0c382017-02-17 16:37:26464 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
465 return LegacyTabHelper::GetTabForWebState(
sdefresne365a73a2017-03-14 15:07:13466 _webStateList->GetWebStateAt(static_cast<int>(index)));
sdefresne67dfdd62016-12-19 12:43:12467}
468
469- (NSUInteger)indexOfTab:(Tab*)tab {
sdefresne365a73a2017-03-14 15:07:13470 int index = _webStateList->GetIndexOfWebState(tab.webState);
sdefresne32c0c382017-02-17 16:37:26471 if (index == WebStateList::kInvalidIndex)
472 return NSNotFound;
473
474 DCHECK_GE(index, 0);
475 return static_cast<NSUInteger>(index);
sdefresne67dfdd62016-12-19 12:43:12476}
477
sdefresne67dfdd62016-12-19 12:43:12478- (Tab*)openerOfTab:(Tab*)tab {
sdefresne365a73a2017-03-14 15:07:13479 int index = _webStateList->GetIndexOfWebState(tab.webState);
sdefresnee0a9ba62017-02-23 09:14:11480 if (index == WebStateList::kInvalidIndex)
sdefresne67dfdd62016-12-19 12:43:12481 return nil;
sdefresnee0a9ba62017-02-23 09:14:11482
sdefresne500f5222017-03-24 14:01:40483 WebStateOpener opener = _webStateList->GetOpenerOfWebStateAt(index);
484 return opener.opener ? LegacyTabHelper::GetTabForWebState(opener.opener)
485 : nil;
sdefresne67dfdd62016-12-19 12:43:12486}
487
sdefresnea6395912017-03-01 01:14:35488- (Tab*)insertTabWithURL:(const GURL&)URL
489 referrer:(const web::Referrer&)referrer
490 transition:(ui::PageTransition)transition
491 opener:(Tab*)parentTab
492 openedByDOM:(BOOL)openedByDOM
493 atIndex:(NSUInteger)index
494 inBackground:(BOOL)inBackground {
sdefresne67dfdd62016-12-19 12:43:12495 web::NavigationManager::WebLoadParams params(URL);
496 params.referrer = referrer;
497 params.transition_type = transition;
sdefresnea6395912017-03-01 01:14:35498 return [self insertTabWithLoadParams:params
499 opener:parentTab
500 openedByDOM:openedByDOM
501 atIndex:index
502 inBackground:inBackground];
sdefresne67dfdd62016-12-19 12:43:12503}
504
sdefresnea6395912017-03-01 01:14:35505- (Tab*)insertTabWithLoadParams:
sdefresne2c600c52017-04-04 16:49:59506 (const web::NavigationManager::WebLoadParams&)loadParams
sdefresnea6395912017-03-01 01:14:35507 opener:(Tab*)parentTab
508 openedByDOM:(BOOL)openedByDOM
509 atIndex:(NSUInteger)index
510 inBackground:(BOOL)inBackground {
511 DCHECK(_browserState);
sdefresne67dfdd62016-12-19 12:43:12512
Sylvain Defresne419a7a72017-08-09 16:27:37513 int insertionIndex = WebStateList::kInvalidIndex;
514 int insertionFlags = WebStateList::INSERT_NO_FLAGS;
515 if (index != TabModelConstants::kTabPositionAutomatically) {
sdefresne2c600c52017-04-04 16:49:59516 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne419a7a72017-08-09 16:27:37517 insertionIndex = static_cast<int>(index);
518 insertionFlags |= WebStateList::INSERT_FORCE_INDEX;
519 } else if (!ui::PageTransitionCoreTypeIs(loadParams.transition_type,
520 ui::PAGE_TRANSITION_LINK)) {
521 insertionIndex = _webStateList->count();
522 insertionFlags |= WebStateList::INSERT_FORCE_INDEX;
sdefresne2c600c52017-04-04 16:49:59523 }
524
Sylvain Defresne2b834542017-08-10 14:50:02525 if (!inBackground) {
526 insertionFlags |= WebStateList::INSERT_ACTIVATE;
527 }
Sylvain Defresne419a7a72017-08-09 16:27:37528
Sylvain Defresne2b834542017-08-10 14:50:02529 web::WebState::CreateParams createParams(self.browserState);
530 createParams.created_with_opener = openedByDOM;
sdefresne2c600c52017-04-04 16:49:59531
Sylvain Defresne2b834542017-08-10 14:50:02532 std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams);
Sylvain Defresne419a7a72017-08-09 16:27:37533 webState->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresne2c600c52017-04-04 16:49:59534
Sylvain Defresne2b834542017-08-10 14:50:02535 insertionIndex = _webStateList->InsertWebState(
536 insertionIndex, std::move(webState), insertionFlags,
537 WebStateOpener(parentTab.webState));
sdefresne7d699dd2017-04-05 13:05:23538
Sylvain Defresne2b834542017-08-10 14:50:02539 return LegacyTabHelper::GetTabForWebState(
540 _webStateList->GetWebStateAt(insertionIndex));
sdefresne67dfdd62016-12-19 12:43:12541}
542
sdefresne67dfdd62016-12-19 12:43:12543- (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex {
sdefresne32c0c382017-02-17 16:37:26544 DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX));
sdefresne365a73a2017-03-14 15:07:13545 int fromIndex = _webStateList->GetIndexOfWebState(tab.webState);
546 _webStateList->MoveWebStateAt(fromIndex, static_cast<int>(toIndex));
sdefresne67dfdd62016-12-19 12:43:12547}
548
sdefresne67dfdd62016-12-19 12:43:12549- (void)closeTabAtIndex:(NSUInteger)index {
sdefresne2c600c52017-04-04 16:49:59550 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne6b776d72017-10-18 21:14:06551 _webStateList->CloseWebStateAt(static_cast<int>(index),
552 WebStateList::CLOSE_USER_ACTION);
sdefresne67dfdd62016-12-19 12:43:12553}
554
555- (void)closeTab:(Tab*)tab {
sdefresne2c600c52017-04-04 16:49:59556 [self closeTabAtIndex:[self indexOfTab:tab]];
sdefresne67dfdd62016-12-19 12:43:12557}
558
559- (void)closeAllTabs {
Sylvain Defresne6b776d72017-10-18 21:14:06560 _webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION);
Mark Cogan3c0524312018-01-02 16:00:45561 [_observers tabModelClosedAllTabs:self];
sdefresne67dfdd62016-12-19 12:43:12562}
563
564- (void)haltAllTabs {
Sylvain Defresne5b1174cb2018-01-16 15:47:58565 for (int index = 0; index < _webStateList->count(); ++index) {
566 web::WebState* webState = _webStateList->GetWebStateAt(index);
567 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
sdefresne67dfdd62016-12-19 12:43:12568 [tab terminateNetworkActivity];
569 }
570}
571
572- (void)notifyTabChanged:(Tab*)tab {
573 [_observers tabModel:self didChangeTab:tab];
574}
575
Mark Cogan5b494642018-01-02 12:55:46576- (void)notifyTabLoading:(Tab*)tab {
577 [_observers tabModel:self willStartLoadingTab:tab];
578 [self notifyTabChanged:tab];
579 [_observers tabModel:self didStartLoadingTab:tab];
580}
581
582- (void)notifyTabFinishedLoading:(Tab*)tab success:(BOOL)success {
583 [self notifyTabChanged:tab];
584 [_observers tabModel:self didFinishLoadingTab:tab success:success];
585}
586
587- (void)notifyNewTabWillOpen:(Tab*)tab inBackground:(BOOL)background {
588 [_observers tabModel:self newTabWillOpen:tab inBackground:background];
589}
590
591- (void)notifyTabWasDeselected:(Tab*)tab {
592 [_observers tabModel:self didDeselectTab:tab];
593}
594
sdefresne67dfdd62016-12-19 12:43:12595- (void)addObserver:(id<TabModelObserver>)observer {
596 [_observers addObserver:observer];
597}
598
599- (void)removeObserver:(id<TabModelObserver>)observer {
600 [_observers removeObserver:observer];
601}
602
603- (void)resetSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46604 if (_webStateListMetricsObserver)
605 _webStateListMetricsObserver->ResetSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12606}
607
608- (void)recordSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46609 if (_webStateListMetricsObserver)
610 _webStateListMetricsObserver->RecordSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12611}
612
613- (void)notifyTabSnapshotChanged:(Tab*)tab withImage:(UIImage*)image {
614 DCHECK([NSThread isMainThread]);
615 [_observers tabModel:self didChangeTabSnapshot:tab withImage:image];
616}
617
sdefresne67dfdd62016-12-19 12:43:12618- (void)setWebUsageEnabled:(BOOL)webUsageEnabled {
sdefresnefd2aa652017-05-23 14:23:10619 if (_webUsageEnabled == webUsageEnabled)
sdefresne67dfdd62016-12-19 12:43:12620 return;
sdefresnefd2aa652017-05-23 14:23:10621 _webUsageEnabled = webUsageEnabled;
sdefresne7d699dd2017-04-05 13:05:23622 for (int index = 0; index < _webStateList->count(); ++index) {
623 web::WebState* webState = _webStateList->GetWebStateAt(index);
sdefresnefd2aa652017-05-23 14:23:10624 webState->SetWebUsageEnabled(_webUsageEnabled ? true : false);
sdefresne67dfdd62016-12-19 12:43:12625 }
626}
627
628- (void)setPrimary:(BOOL)primary {
Sylvain Defresne890975f52017-08-24 17:42:26629 if (_tabUsageRecorder) {
630 _tabUsageRecorder->RecordPrimaryTabModelChange(primary,
631 self.currentTab.webState);
632 }
sdefresne67dfdd62016-12-19 12:43:12633}
634
635- (NSSet*)currentlyReferencedExternalFiles {
636 NSMutableSet* referencedFiles = [NSMutableSet set];
637 if (!_browserState)
638 return referencedFiles;
639 // Check the currently open tabs for external files.
Sylvain Defresne5b1174cb2018-01-16 15:47:58640 for (int index = 0; index < _webStateList->count(); ++index) {
641 web::WebState* webState = _webStateList->GetWebStateAt(index);
642 const GURL& lastCommittedURL = webState->GetLastCommittedURL();
kkhorimoto2a192b82017-06-01 20:11:08643 if (UrlIsExternalFileReference(lastCommittedURL)) {
644 [referencedFiles addObject:base::SysUTF8ToNSString(
645 lastCommittedURL.ExtractFileName())];
646 }
647 web::NavigationItem* pendingItem =
Sylvain Defresne5b1174cb2018-01-16 15:47:58648 webState->GetNavigationManager()->GetPendingItem();
kkhorimoto2a192b82017-06-01 20:11:08649 if (pendingItem && UrlIsExternalFileReference(pendingItem->GetURL())) {
650 [referencedFiles addObject:base::SysUTF8ToNSString(
651 pendingItem->GetURL().ExtractFileName())];
sdefresne67dfdd62016-12-19 12:43:12652 }
653 }
654 // Do the same for the recently closed tabs.
655 sessions::TabRestoreService* restoreService =
656 IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState);
657 DCHECK(restoreService);
658 for (const auto& entry : restoreService->entries()) {
659 sessions::TabRestoreService::Tab* tab =
660 static_cast<sessions::TabRestoreService::Tab*>(entry.get());
661 int navigationIndex = tab->current_navigation_index;
662 sessions::SerializedNavigationEntry navigation =
663 tab->navigations[navigationIndex];
664 GURL URL = navigation.virtual_url();
665 if (UrlIsExternalFileReference(URL)) {
666 NSString* fileName = base::SysUTF8ToNSString(URL.ExtractFileName());
667 [referencedFiles addObject:fileName];
668 }
669 }
670 return referencedFiles;
671}
672
673// NOTE: This can be called multiple times, so must be robust against that.
674- (void)browserStateDestroyed {
sdefresne05315352017-05-23 14:46:17675 if (!_browserState)
676 return;
677
sdefresne67dfdd62016-12-19 12:43:12678 [[NSNotificationCenter defaultCenter] removeObserver:self];
Mohamad Ahmadie04d0952017-11-17 23:31:24679 TabModelList::UnregisterTabModelFromChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12680 _browserState = nullptr;
sdefresne05315352017-05-23 14:46:17681
Sylvain Defresnee8ae6ad2017-09-14 14:22:10682 // Clear weak pointer to observers before destroying them.
683 _tabModelNotificationObserver = nullptr;
sdefresne05315352017-05-23 14:46:17684 _webStateListMetricsObserver = nullptr;
685
686 // Close all tabs. Do this in an @autoreleasepool as WebStateList observers
687 // will be notified (they are unregistered later). As some of them may be
688 // implemented in Objective-C and unregister themselves in their -dealloc
689 // method, ensure they -autorelease introduced by ARC are processed before
690 // the WebStateList destructor is called.
691 @autoreleasepool {
Sylvain Defresne6b776d72017-10-18 21:14:06692 _webStateList->CloseAllWebStates(WebStateList::CLOSE_NO_FLAGS);
sdefresne05315352017-05-23 14:46:17693 }
694
695 // Unregister all observers after closing all the tabs as some of them are
696 // required to properly clean up the Tabs.
697 for (const auto& webStateListObserver : _webStateListObservers)
698 _webStateList->RemoveObserver(webStateListObserver.get());
699 _webStateListObservers.clear();
700 _retainedWebStateListObservers = nil;
701
702 _clearPoliciesTaskTracker.TryCancelAll();
Sylvain Defresne890975f52017-08-24 17:42:26703 _tabUsageRecorder.reset();
Sylvain Defresne94394332018-01-22 13:11:00704 _webStateObserver.reset();
sdefresne67dfdd62016-12-19 12:43:12705}
706
sdefresne573bd7b2017-03-03 00:12:49707- (void)navigationCommittedInTab:(Tab*)tab
708 previousItem:(web::NavigationItem*)previousItem {
sdefresne67dfdd62016-12-19 12:43:12709 if (self.offTheRecord)
710 return;
711 if (![tab navigationManager])
712 return;
713
714 // See if the navigation was within a page; if so ignore it.
sdefresne67dfdd62016-12-19 12:43:12715 if (previousItem) {
716 GURL previousURL = previousItem->GetURL();
717 GURL currentURL = [tab navigationManager]->GetVisibleItem()->GetURL();
718
719 url::Replacements<char> replacements;
720 replacements.ClearRef();
721 if (previousURL.ReplaceComponents(replacements) ==
722 currentURL.ReplaceComponents(replacements)) {
723 return;
724 }
725 }
726
727 int tabCount = static_cast<int>(self.count);
728 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tabCount, 1, 200, 50);
729}
730
sdefresne67dfdd62016-12-19 12:43:12731#pragma mark - Private methods
732
sdefresnedcc6c6e2017-04-19 15:49:02733- (SessionIOS*)sessionForSaving {
sdefresne67dfdd62016-12-19 12:43:12734 // Build the array of sessions. Copy the session objects as the saving will
735 // be done on a separate thread.
736 // TODO(crbug.com/661986): This could get expensive especially since this
737 // window may never be saved (if another call comes in before the delay).
sdefresne68f546a2017-04-20 10:54:03738 return [[SessionIOS alloc]
739 initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]];
sdefresne67dfdd62016-12-19 12:43:12740}
741
sdefresne44c09dc2017-02-07 11:13:08742- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window
743 persistState:(BOOL)persistState {
744 DCHECK(_browserState);
745 DCHECK(window);
746
Mark Cogan5b494642018-01-02 12:55:46747 // Disable calling -notifyNewTabWillOpen: while restoring a session as it
748 // breaks the BVC (see crbug.com/763964).
Sylvain Defresnee8ae6ad2017-09-14 14:22:10749 base::ScopedClosureRunner enableTabModelNotificationObserver;
750 if (_tabModelNotificationObserver) {
751 _tabModelNotificationObserver->SetDisabled(true);
752 enableTabModelNotificationObserver.ReplaceClosure(
753 base::BindOnce(&TabModelNotificationObserver::SetDisabled,
754 base::Unretained(_tabModelNotificationObserver), false));
755 }
756
sdefresne68f546a2017-04-20 10:54:03757 if (!window.sessions.count)
sdefresne44c09dc2017-02-07 11:13:08758 return NO;
759
sdefresne365a73a2017-03-14 15:07:13760 int oldCount = _webStateList->count();
sdefresne32c0c382017-02-17 16:37:26761 DCHECK_GE(oldCount, 0);
762
sdefresne08c0ab42017-04-05 12:49:43763 web::WebState::CreateParams createParams(_browserState);
764 DeserializeWebStateList(
sdefresnefd2aa652017-05-23 14:23:10765 _webStateList.get(), window,
Sylvain Defresne2b834542017-08-10 14:50:02766 base::BindRepeating(&web::WebState::CreateWithStorageSession,
767 createParams));
sdefresne08c0ab42017-04-05 12:49:43768
769 DCHECK_GT(_webStateList->count(), oldCount);
770 int restoredCount = _webStateList->count() - oldCount;
sdefresne68f546a2017-04-20 10:54:03771 DCHECK_EQ(window.sessions.count, static_cast<NSUInteger>(restoredCount));
sdefresne08c0ab42017-04-05 12:49:43772
sdefresnec92bada2017-02-08 14:43:49773 scoped_refptr<web::CertificatePolicyCache> policyCache =
774 web::BrowserState::GetCertificatePolicyCache(_browserState);
sdefresne44c09dc2017-02-07 11:13:08775
Sylvain Defresne890975f52017-08-24 17:42:26776 std::vector<web::WebState*> restoredWebStates;
777 if (_tabUsageRecorder)
778 restoredWebStates.reserve(window.sessions.count);
sdefresnee0a9ba62017-02-23 09:14:11779
sdefresne2c600c52017-04-04 16:49:59780 for (int index = oldCount; index < _webStateList->count(); ++index) {
781 web::WebState* webState = _webStateList->GetWebStateAt(index);
Sylvain Defresne7178d4c2017-09-14 13:22:37782 web::NavigationItem* visible_item =
783 webState->GetNavigationManager()->GetVisibleItem();
Gauthier Ambard32fe0dd2017-09-20 14:25:17784
785 if (!(visible_item &&
786 visible_item->GetVirtualURL() == GURL(kChromeUINewTabURL))) {
787 PagePlaceholderTabHelper::FromWebState(webState)
788 ->AddPlaceholderForNextNavigation();
789 }
790
Sylvain Defresne7178d4c2017-09-14 13:22:37791 if (visible_item && visible_item->GetVirtualURL().is_valid()) {
792 favicon::WebFaviconDriver::FromWebState(webState)->FetchFavicon(
793 visible_item->GetVirtualURL(), /*is_same_document=*/false);
794 }
795
sdefresne44c09dc2017-02-07 11:13:08796 // Restore the CertificatePolicyCache (note that webState is invalid after
sdefresnebf75f022017-03-07 17:48:35797 // passing it via move semantic to -initWithWebState:model:).
Eugene Butb56ecad2017-08-10 21:09:52798 UpdateCertificatePolicyCacheFromWebState(policyCache, webState);
Sylvain Defresne890975f52017-08-24 17:42:26799
800 if (_tabUsageRecorder)
801 restoredWebStates.push_back(webState);
sdefresne44c09dc2017-02-07 11:13:08802 }
sdefresnee0a9ba62017-02-23 09:14:11803
sdefresne44c09dc2017-02-07 11:13:08804 // If there was only one tab and it was the new tab page, clobber it.
805 BOOL closedNTPTab = NO;
806 if (oldCount == 1) {
sdefresne32c0c382017-02-17 16:37:26807 Tab* tab = [self tabAtIndex:0];
kkhorimoto2a192b82017-06-01 20:11:08808 BOOL hasPendingLoad =
809 tab.webState->GetNavigationManager()->GetPendingItem() != nullptr;
Sylvain Defresnee7f2c8a2017-10-17 02:39:19810 if (!hasPendingLoad &&
811 tab.webState->GetLastCommittedURL() == kChromeUINewTabURL) {
sdefresne44c09dc2017-02-07 11:13:08812 [self closeTab:tab];
813 closedNTPTab = YES;
814 oldCount = 0;
815 }
816 }
Sylvain Defresne890975f52017-08-24 17:42:26817 if (_tabUsageRecorder) {
818 _tabUsageRecorder->InitialRestoredTabs(self.currentTab.webState,
819 restoredWebStates);
820 }
Sylvain Defresnee8ae6ad2017-09-14 14:22:10821
sdefresne44c09dc2017-02-07 11:13:08822 return closedNTPTab;
823}
824
sdefresne67dfdd62016-12-19 12:43:12825#pragma mark - Notification Handlers
826
827// Called when UIApplicationWillResignActiveNotification is received.
828- (void)willResignActive:(NSNotification*)notify {
sdefresnefd2aa652017-05-23 14:23:10829 if (_webUsageEnabled && self.currentTab) {
Sylvain Defresne528c17b2017-06-08 15:00:21830 [SnapshotCacheFactory::GetForBrowserState(_browserState)
sdefresne2f7781c2017-03-02 19:12:46831 willBeSavedGreyWhenBackgrounding:self.currentTab.tabId];
sdefresne67dfdd62016-12-19 12:43:12832 }
833}
834
835// Called when UIApplicationDidEnterBackgroundNotification is received.
836- (void)applicationDidEnterBackground:(NSNotification*)notify {
837 if (!_browserState)
838 return;
sdefresnec92bada2017-02-08 14:43:49839
sdefresne67dfdd62016-12-19 12:43:12840 // Evict all the certificate policies except for the current entries of the
841 // active sessions.
sdefresnec92bada2017-02-08 14:43:49842 CleanCertificatePolicyCache(
843 &_clearPoliciesTaskTracker,
844 web::WebThread::GetTaskRunnerForThread(web::WebThread::IO),
sdefresne32c0c382017-02-17 16:37:26845 web::BrowserState::GetCertificatePolicyCache(_browserState),
sdefresne365a73a2017-03-14 15:07:13846 _webStateList.get());
sdefresne67dfdd62016-12-19 12:43:12847
sdefresne67dfdd62016-12-19 12:43:12848 // Normally, the session is saved after some timer expires but since the app
849 // is about to enter the background send YES to save the session immediately.
850 [self saveSessionImmediately:YES];
851
852 // Write out a grey version of the current website to disk.
sdefresnefd2aa652017-05-23 14:23:10853 if (_webUsageEnabled && self.currentTab) {
Sylvain Defresne528c17b2017-06-08 15:00:21854 [SnapshotCacheFactory::GetForBrowserState(_browserState)
sdefresne2f7781c2017-03-02 19:12:46855 saveGreyInBackgroundForSessionID:self.currentTab.tabId];
sdefresne67dfdd62016-12-19 12:43:12856 }
857}
858
Sylvain Defresne94394332018-01-22 13:11:00859#pragma mark - CRWWebStateObserver
860
861- (void)webState:(web::WebState*)webState
862 didCommitNavigationWithDetails:
863 (const web::LoadCommittedDetails&)load_details {
864 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
865 [self notifyTabChanged:tab];
866
867 web::NavigationItem* previousItem = nullptr;
868 if (load_details.previous_item_index >= 0) {
869 DCHECK(webState->GetNavigationManager());
870 previousItem = webState->GetNavigationManager()->GetItemAtIndex(
871 load_details.previous_item_index);
872 }
873
874 [self navigationCommittedInTab:tab previousItem:previousItem];
875
876 // Sending a notification about the url change for crash reporting.
877 // TODO(crbug.com/661675): Consider using the navigation entry committed
878 // notification now that it's in the right place.
879 const std::string& lastCommittedURL = webState->GetLastCommittedURL().spec();
880 if (!lastCommittedURL.empty()) {
881 [[NSNotificationCenter defaultCenter]
882 postNotificationName:kTabUrlStartedLoadingNotificationForCrashReporting
883 object:tab
884 userInfo:@{
885 kTabUrlKey : base::SysUTF8ToNSString(lastCommittedURL)
886 }];
887 }
888}
889
890- (void)webState:(web::WebState*)webState
891 didStartNavigation:(web::NavigationContext*)navigation {
892 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
893
894 // In order to avoid false positive in the crash loop detection, disable the
895 // counter as soon as an URL is loaded. This requires an user action and is a
896 // significant source of crashes. Ignore NTP as it is loaded by default after
897 // a crash.
898 if (navigation->GetUrl().host_piece() != kChromeUINewTabHost) {
899 static dispatch_once_t dispatch_once_token;
900 dispatch_once(&dispatch_once_token, ^{
901 crash_util::ResetFailedStartupAttemptCount();
902 });
903 }
904
905 if (_tabUsageRecorder && ShouldRecordPageLoadStartForNavigation(navigation)) {
906 _tabUsageRecorder->RecordPageLoadStart(webState);
907 }
908
909 [self notifyTabChanged:tab];
910 [[NSNotificationCenter defaultCenter]
911 postNotificationName:
912 kTabClosingCurrentDocumentNotificationForCrashReporting
913 object:tab];
914
915 DCHECK(webState->GetNavigationManager());
916 web::NavigationItem* navigationItem =
917 webState->GetNavigationManager()->GetPendingItem();
918
919 // TODO(crbug.com/676129): the pending item is not correctly set when the
920 // page is reloading, use the last committed item if pending item is null.
921 // Remove this once tracking bug is fixed.
922 if (!navigationItem) {
923 navigationItem = webState->GetNavigationManager()->GetLastCommittedItem();
924 }
925
926 [[OmniboxGeolocationController sharedInstance]
927 addLocationToNavigationItem:navigationItem
928 browserState:ios::ChromeBrowserState::FromBrowserState(
929 webState->GetBrowserState())];
930}
931
932- (void)webState:(web::WebState*)webState
933 didFinishNavigation:(web::NavigationContext*)navigation {
934 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
935 [self notifyTabChanged:tab];
936}
937
938- (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
939 DCHECK(!webState->IsLoading());
940 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
941 [self notifyTabFinishedLoading:tab success:success];
942
943 RecordInterfaceOrientationMetric();
944 RecordMainFrameNavigationMetric(webState);
945
946 [[OmniboxGeolocationController sharedInstance] finishPageLoadForTab:tab
947 loadSuccess:success];
948}
949
950- (void)webState:(web::WebState*)webState
951 didChangeLoadingProgress:(double)progress {
952 // TODO(crbug.com/546406): It is probably possible to do something smarter,
953 // but the fact that this is not always sent will have to be taken into
954 // account.
955 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
956 [self notifyTabChanged:tab];
957}
958
959- (void)webStateDidChangeTitle:(web::WebState*)webState {
960 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
961 [self notifyTabChanged:tab];
962}
963
964- (void)webStateDidChangeVisibleSecurityState:(web::WebState*)webState {
965 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
966 [self notifyTabChanged:tab];
967}
968
969- (void)webStateDestroyed:(web::WebState*)webState {
970 // The TabModel is removed from WebState's observer when the WebState is
971 // detached from WebStateList which happens before WebState destructor,
972 // so this method should never be called.
973 NOTREACHED();
974}
975
976- (void)webStateDidStopLoading:(web::WebState*)webState {
977 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
978 [self notifyTabChanged:tab];
979}
980
981#pragma mark - WebStateListObserving
982
983- (void)webStateList:(WebStateList*)webStateList
984 didInsertWebState:(web::WebState*)webState
985 atIndex:(int)index
986 activating:(BOOL)activating {
987 DCHECK(webState);
988 webState->AddObserver(_webStateObserver.get());
989}
990
991- (void)webStateList:(WebStateList*)webStateList
992 didReplaceWebState:(web::WebState*)oldWebState
993 withWebState:(web::WebState*)newWebState
994 atIndex:(int)atIndex {
995 DCHECK(oldWebState);
996 DCHECK(newWebState);
997 newWebState->AddObserver(_webStateObserver.get());
998 oldWebState->RemoveObserver(_webStateObserver.get());
999}
1000
1001- (void)webStateList:(WebStateList*)webStateList
1002 didDetachWebState:(web::WebState*)webState
1003 atIndex:(int)atIndex {
1004 DCHECK(webState);
1005 webState->RemoveObserver(_webStateObserver.get());
1006}
1007
sdefresne67dfdd62016-12-19 12:43:121008@end