blob: b4d40e925ab12a2e0f2d2d5b9b3b7dc306eb57eb [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"
sdefresne67dfdd62016-12-19 12:43:1223#include "components/sessions/core/serialized_navigation_entry.h"
24#include "components/sessions/core/session_id.h"
25#include "components/sessions/core/tab_restore_service.h"
26#include "components/sessions/ios/ios_live_tab.h"
27#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
28#include "ios/chrome/browser/chrome_url_constants.h"
29#import "ios/chrome/browser/chrome_url_util.h"
Sylvain Defresne94394332018-01-22 13:11:0030#include "ios/chrome/browser/crash_loop_detection_util.h"
31#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
sdefresne67dfdd62016-12-19 12:43:1232#import "ios/chrome/browser/metrics/tab_usage_recorder.h"
Sylvain Defresne890975f52017-08-24 17:42:2633#import "ios/chrome/browser/prerender/prerender_service_factory.h"
sdefresne67dfdd62016-12-19 12:43:1234#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
sdefresnedcc6c6e2017-04-19 15:49:0235#import "ios/chrome/browser/sessions/session_ios.h"
sdefresneda974c82017-04-06 17:24:3936#import "ios/chrome/browser/sessions/session_service_ios.h"
sdefresne56528152017-03-22 17:03:2837#import "ios/chrome/browser/sessions/session_window_ios.h"
sdefresne67dfdd62016-12-19 12:43:1238#import "ios/chrome/browser/snapshots/snapshot_cache.h"
Sylvain Defresne528c17b2017-06-08 15:00:2139#import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
sdefresne67dfdd62016-12-19 12:43:1240#include "ios/chrome/browser/tab_parenting_global_observer.h"
sdefresne32c0c382017-02-17 16:37:2641#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
sdefresne67dfdd62016-12-19 12:43:1242#import "ios/chrome/browser/tabs/tab.h"
sdefresne2c600c52017-04-04 16:49:5943#import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h"
Sylvain Defresne17b7b33f2017-11-10 19:55:1544#import "ios/chrome/browser/tabs/tab_model_favicon_driver_observer.h"
sdefresne8cfdf8f2017-01-11 18:22:1445#import "ios/chrome/browser/tabs/tab_model_list.h"
Sylvain Defresne2b834542017-08-10 14:50:0246#import "ios/chrome/browser/tabs/tab_model_notification_observer.h"
sdefresne0a762e42017-02-14 11:11:2547#import "ios/chrome/browser/tabs/tab_model_observers.h"
sdefresne39bfaab2017-02-20 11:06:3648#import "ios/chrome/browser/tabs/tab_model_observers_bridge.h"
sdefresne2f7781c2017-03-02 19:12:4649#import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h"
sdefresne67dfdd62016-12-19 12:43:1250#import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h"
sdefresne365a73a2017-03-14 15:07:1351#import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.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"
Kurt Horimoto070e40b2018-08-31 20:50:0059#import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler.h"
60#import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler_factory.h"
sdefresne67dfdd62016-12-19 12:43:1261#include "ios/web/public/browser_state.h"
62#include "ios/web/public/certificate_policy_cache.h"
sdefresne67dfdd62016-12-19 12:43:1263#include "ios/web/public/navigation_item.h"
64#import "ios/web/public/navigation_manager.h"
sdefresne2c600c52017-04-04 16:49:5965#import "ios/web/public/serializable_user_data_manager.h"
Sylvain Defresne94394332018-01-22 13:11:0066#import "ios/web/public/web_state/navigation_context.h"
kkhorimotod3a3986f2017-04-05 02:21:4167#include "ios/web/public/web_state/session_certificate_policy_cache.h"
Sylvain Defresne94394332018-01-22 13:11:0068#import "ios/web/public/web_state/web_state_observer_bridge.h"
Eric Seckler217006b2018-09-11 15:21:0269#include "ios/web/public/web_task_traits.h"
sdefresne67dfdd62016-12-19 12:43:1270#include "ios/web/public/web_thread.h"
sdefresne67dfdd62016-12-19 12:43:1271#include "url/gurl.h"
72
sdefresne4162a4dc2017-05-15 11:26:2473#if !defined(__has_feature) || !__has_feature(objc_arc)
74#error "This file requires ARC support."
75#endif
76
sdefresne67dfdd62016-12-19 12:43:1277namespace {
78
79// Updates CRWSessionCertificatePolicyManager's certificate policy cache.
sdefresnec92bada2017-02-08 14:43:4980void UpdateCertificatePolicyCacheFromWebState(
81 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
kkhorimotod3a3986f2017-04-05 02:21:4182 const web::WebState* web_state) {
sdefresne4c8bf632017-01-26 15:46:4783 DCHECK(web_state);
sdefresnec92bada2017-02-08 14:43:4984 DCHECK_CURRENTLY_ON(web::WebThread::UI);
kkhorimotod3a3986f2017-04-05 02:21:4185 web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache(
86 policy_cache);
sdefresne67dfdd62016-12-19 12:43:1287}
88
sdefresne32c0c382017-02-17 16:37:2689// Populates the certificate policy cache based on the WebStates of
90// |web_state_list|.
sdefresnec92bada2017-02-08 14:43:4991void RestoreCertificatePolicyCacheFromModel(
92 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:2693 WebStateList* web_state_list) {
sdefresnec92bada2017-02-08 14:43:4994 DCHECK_CURRENTLY_ON(web::WebThread::UI);
sdefresne32c0c382017-02-17 16:37:2695 for (int index = 0; index < web_state_list->count(); ++index) {
96 UpdateCertificatePolicyCacheFromWebState(
97 policy_cache, web_state_list->GetWebStateAt(index));
98 }
sdefresne67dfdd62016-12-19 12:43:1299}
100
sdefresnec92bada2017-02-08 14:43:49101// Scrubs the certificate policy cache of all certificates policies except
sdefresne32c0c382017-02-17 16:37:26102// those for the current entries in |web_state_list|.
sdefresne67dfdd62016-12-19 12:43:12103void CleanCertificatePolicyCache(
sdefresnec92bada2017-02-08 14:43:49104 base::CancelableTaskTracker* task_tracker,
105 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
106 const scoped_refptr<web::CertificatePolicyCache>& policy_cache,
sdefresne32c0c382017-02-17 16:37:26107 WebStateList* web_state_list) {
sdefresne67dfdd62016-12-19 12:43:12108 DCHECK(policy_cache);
sdefresne32c0c382017-02-17 16:37:26109 DCHECK(web_state_list);
sdefresnec92bada2017-02-08 14:43:49110 DCHECK_CURRENTLY_ON(web::WebThread::UI);
111 task_tracker->PostTaskAndReply(
112 task_runner.get(), FROM_HERE,
113 base::Bind(&web::CertificatePolicyCache::ClearCertificatePolicies,
114 policy_cache),
115 base::Bind(&RestoreCertificatePolicyCacheFromModel, policy_cache,
sdefresne32c0c382017-02-17 16:37:26116 base::Unretained(web_state_list)));
sdefresne67dfdd62016-12-19 12:43:12117}
118
Sylvain Defresne94394332018-01-22 13:11:00119// Returns whether |rhs| and |lhs| are different user agent types. If either
120// of them is web::UserAgentType::NONE, then return NO.
121BOOL IsTransitionBetweenDesktopAndMobileUserAgent(web::UserAgentType lhs,
122 web::UserAgentType rhs) {
123 if (lhs == web::UserAgentType::NONE)
124 return NO;
125
126 if (rhs == web::UserAgentType::NONE)
127 return NO;
128
129 return lhs != rhs;
130}
131
132// Returns whether TabUsageRecorder::RecordPageLoadStart should be called for
133// the given navigation.
134BOOL ShouldRecordPageLoadStartForNavigation(
135 web::NavigationContext* navigation) {
136 web::NavigationManager* navigation_manager =
137 navigation->GetWebState()->GetNavigationManager();
138
139 web::NavigationItem* last_committed_item =
140 navigation_manager->GetLastCommittedItem();
141 if (!last_committed_item) {
142 // Opening a child window and loading URL there.
143 // http://crbug.com/773160
144 return NO;
145 }
146
147 web::NavigationItem* pending_item = navigation_manager->GetPendingItem();
148 if (pending_item) {
149 if (IsTransitionBetweenDesktopAndMobileUserAgent(
150 pending_item->GetUserAgentType(),
151 last_committed_item->GetUserAgentType())) {
152 // Switching between Desktop and Mobile user agent.
153 return NO;
154 }
155 }
156
157 ui::PageTransition transition = navigation->GetPageTransition();
158 if (!ui::PageTransitionIsNewNavigation(transition)) {
159 // Back/forward navigation or reload.
160 return NO;
161 }
162
163 if ((transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0) {
164 // Client redirect.
165 return NO;
166 }
167
168 static const ui::PageTransition kRecordedPageTransitionTypes[] = {
169 ui::PAGE_TRANSITION_TYPED,
170 ui::PAGE_TRANSITION_LINK,
171 ui::PAGE_TRANSITION_GENERATED,
172 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
173 ui::PAGE_TRANSITION_FORM_SUBMIT,
174 ui::PAGE_TRANSITION_KEYWORD,
175 ui::PAGE_TRANSITION_KEYWORD_GENERATED,
176 };
177
kokihoon189a4022018-10-13 02:43:44178 for (size_t i = 0; i < base::size(kRecordedPageTransitionTypes); ++i) {
Sylvain Defresne94394332018-01-22 13:11:00179 const ui::PageTransition recorded_type = kRecordedPageTransitionTypes[i];
180 if (ui::PageTransitionCoreTypeIs(transition, recorded_type)) {
181 return YES;
182 }
183 }
184
185 return NO;
186}
187
188// Records metrics for the interface's orientation.
189void RecordInterfaceOrientationMetric() {
190 switch ([[UIApplication sharedApplication] statusBarOrientation]) {
191 case UIInterfaceOrientationPortrait:
192 case UIInterfaceOrientationPortraitUpsideDown:
193 UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", YES);
194 break;
195 case UIInterfaceOrientationLandscapeLeft:
196 case UIInterfaceOrientationLandscapeRight:
197 UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", NO);
198 break;
199 case UIInterfaceOrientationUnknown:
200 // TODO(crbug.com/228832): Convert from a boolean histogram to an
201 // enumerated histogram and log this case as well.
202 break;
203 }
204}
205
206// Records metrics for main frame navigation.
207void RecordMainFrameNavigationMetric(web::WebState* web_state) {
208 DCHECK(web_state);
209 DCHECK(web_state->GetBrowserState());
210 DCHECK(web_state->GetNavigationManager());
211 web::NavigationItem* item =
212 web_state->GetNavigationManager()->GetLastCommittedItem();
213 navigation_metrics::RecordMainFrameNavigation(
214 item ? item->GetVirtualURL() : GURL::EmptyGURL(), true,
215 web_state->GetBrowserState()->IsOffTheRecord());
216}
217
sdefresne67dfdd62016-12-19 12:43:12218} // anonymous namespace
219
Sylvain Defresne94394332018-01-22 13:11:00220@interface TabModel ()<CRWWebStateObserver, WebStateListObserving> {
sdefresne365a73a2017-03-14 15:07:13221 // Delegate for the WebStateList.
222 std::unique_ptr<WebStateListDelegate> _webStateListDelegate;
223
sdefresne32c0c382017-02-17 16:37:26224 // Underlying shared model implementation.
sdefresne365a73a2017-03-14 15:07:13225 std::unique_ptr<WebStateList> _webStateList;
sdefresne32c0c382017-02-17 16:37:26226
sdefresneee210332017-03-24 10:36:54227 // WebStateListObservers reacting to modifications of the model (may send
228 // notification, translate and forward events, update metrics, ...).
229 std::vector<std::unique_ptr<WebStateListObserver>> _webStateListObservers;
sdefresne39bfaab2017-02-20 11:06:36230
sdefresne3b0155e92017-04-12 15:18:53231 // Strong references to id<WebStateListObserving> wrapped by non-owning
232 // WebStateListObserverBridges.
sdefresne4162a4dc2017-05-15 11:26:24233 NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers;
sdefresne3b0155e92017-04-12 15:18:53234
sdefresne67dfdd62016-12-19 12:43:12235 // The delegate for sync.
236 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate;
sdefresne67dfdd62016-12-19 12:43:12237
Mark Cogan5b494642018-01-02 12:55:46238 // The observer that calls -notifyNewTabWillOpen on this object.
Sylvain Defresnee8ae6ad2017-09-14 14:22:10239 TabModelNotificationObserver* _tabModelNotificationObserver;
240
sdefresne67dfdd62016-12-19 12:43:12241 // Counters for metrics.
sdefresne2f7781c2017-03-02 19:12:46242 WebStateListMetricsObserver* _webStateListMetricsObserver;
sdefresne67dfdd62016-12-19 12:43:12243
244 // Backs up property with the same name.
245 std::unique_ptr<TabUsageRecorder> _tabUsageRecorder;
sdefresne67dfdd62016-12-19 12:43:12246 // Saves session's state.
sdefresne4162a4dc2017-05-15 11:26:24247 SessionServiceIOS* _sessionService;
sdefresne67dfdd62016-12-19 12:43:12248 // List of TabModelObservers.
sdefresne4162a4dc2017-05-15 11:26:24249 TabModelObservers* _observers;
sdefresnec92bada2017-02-08 14:43:49250
251 // Used to ensure thread-safety of the certificate policy management code.
252 base::CancelableTaskTracker _clearPoliciesTaskTracker;
Sylvain Defresne94394332018-01-22 13:11:00253
254 // Used to observe owned Tabs' WebStates.
255 std::unique_ptr<web::WebStateObserver> _webStateObserver;
sdefresne67dfdd62016-12-19 12:43:12256}
257
258// Session window for the contents of the tab model.
sdefresnedcc6c6e2017-04-19 15:49:02259@property(nonatomic, readonly) SessionIOS* sessionForSaving;
Kurt Horimoto070e40b2018-08-31 20:50:00260// Whether the underlying WebStateList's web usage is enabled.
261@property(nonatomic, readonly, getter=isWebUsageEnabled) BOOL webUsageEnabled;
sdefresne67dfdd62016-12-19 12:43:12262
sdefresne67dfdd62016-12-19 12:43:12263@end
264
265@implementation TabModel
266
267@synthesize browserState = _browserState;
sdefresne67dfdd62016-12-19 12:43:12268
269#pragma mark - Overriden
270
271- (void)dealloc {
sdefresne67dfdd62016-12-19 12:43:12272 // browserStateDestroyed should always have been called before destruction.
273 DCHECK(!_browserState);
274
sdefresne05315352017-05-23 14:46:17275 // Make sure the observers do clean after themselves.
276 DCHECK([_observers empty]);
sdefresne67dfdd62016-12-19 12:43:12277}
278
279#pragma mark - Public methods
280
281- (Tab*)currentTab {
sdefresne365a73a2017-03-14 15:07:13282 web::WebState* webState = _webStateList->GetActiveWebState();
sdefresne2f7781c2017-03-02 19:12:46283 return webState ? LegacyTabHelper::GetTabForWebState(webState) : nil;
sdefresne67dfdd62016-12-19 12:43:12284}
285
286- (void)setCurrentTab:(Tab*)newTab {
sdefresne365a73a2017-03-14 15:07:13287 int indexOfTab = _webStateList->GetIndexOfWebState(newTab.webState);
sdefresne2f7781c2017-03-02 19:12:46288 DCHECK_NE(indexOfTab, WebStateList::kInvalidIndex);
sdefresne365a73a2017-03-14 15:07:13289 _webStateList->ActivateWebStateAt(indexOfTab);
sdefresne67dfdd62016-12-19 12:43:12290}
291
292- (TabModelSyncedWindowDelegate*)syncedWindowDelegate {
293 return _syncedWindowDelegate.get();
294}
295
296- (TabUsageRecorder*)tabUsageRecorder {
297 return _tabUsageRecorder.get();
298}
299
300- (BOOL)isOffTheRecord {
301 return _browserState && _browserState->IsOffTheRecord();
302}
303
304- (BOOL)isEmpty {
sdefresne365a73a2017-03-14 15:07:13305 return _webStateList->empty();
sdefresne67dfdd62016-12-19 12:43:12306}
307
sdefresne297553e52017-01-25 17:09:37308- (NSUInteger)count {
sdefresne365a73a2017-03-14 15:07:13309 DCHECK_GE(_webStateList->count(), 0);
310 return static_cast<NSUInteger>(_webStateList->count());
sdefresne297553e52017-01-25 17:09:37311}
312
sdefresneee210332017-03-24 10:36:54313- (WebStateList*)webStateList {
314 return _webStateList.get();
315}
316
Rohit Rao0d9f6c3d2019-01-22 11:29:35317- (instancetype)initWithSessionService:(SessionServiceIOS*)service
318 browserState:(ios::ChromeBrowserState*)browserState {
sdefresne67dfdd62016-12-19 12:43:12319 if ((self = [super init])) {
sdefresne4162a4dc2017-05-15 11:26:24320 _observers = [TabModelObservers observers];
sdefresne67dfdd62016-12-19 12:43:12321
mrefaat294074dc2018-11-27 23:25:20322 _webStateListDelegate = std::make_unique<TabModelWebStateListDelegate>();
Jinho Bangb940d6f42018-01-18 22:39:04323 _webStateList = std::make_unique<WebStateList>(_webStateListDelegate.get());
sdefresne365a73a2017-03-14 15:07:13324
sdefresne67dfdd62016-12-19 12:43:12325 _browserState = browserState;
326 DCHECK(_browserState);
327
Sylvain Defresne94394332018-01-22 13:11:00328 _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
329
sdefresne67dfdd62016-12-19 12:43:12330 // Normal browser states are the only ones to get tab restore. Tab sync
331 // handles incognito browser states by filtering on profile, so it's
332 // important to the backend code to always have a sync window delegate.
333 if (!_browserState->IsOffTheRecord()) {
334 // Set up the usage recorder before tabs are created.
Jinho Bangb940d6f42018-01-18 22:39:04335 _tabUsageRecorder = std::make_unique<TabUsageRecorder>(
Sylvain Defresne890975f52017-08-24 17:42:26336 _webStateList.get(),
337 PrerenderServiceFactory::GetForBrowserState(browserState));
sdefresne67dfdd62016-12-19 12:43:12338 }
Sylvain Defresne72c1e992018-01-15 14:26:17339 _syncedWindowDelegate =
Jinho Bangb940d6f42018-01-18 22:39:04340 std::make_unique<TabModelSyncedWindowDelegate>(_webStateList.get());
sdefresne41971892017-02-23 17:24:14341
342 // There must be a valid session service defined to consume session windows.
343 DCHECK(service);
sdefresne4162a4dc2017-05-15 11:26:24344 _sessionService = service;
sdefresne41971892017-02-23 17:24:14345
sdefresne4162a4dc2017-05-15 11:26:24346 NSMutableArray<id<WebStateListObserving>>* retainedWebStateListObservers =
347 [[NSMutableArray alloc] init];
sdefresne3b0155e92017-04-12 15:18:53348
sdefresne4162a4dc2017-05-15 11:26:24349 TabModelClosingWebStateObserver* tabModelClosingWebStateObserver = [
350 [TabModelClosingWebStateObserver alloc]
351 initWithTabModel:self
352 restoreService:IOSChromeTabRestoreServiceFactory::GetForBrowserState(
353 _browserState)];
sdefresne3b0155e92017-04-12 15:18:53354 [retainedWebStateListObservers addObject:tabModelClosingWebStateObserver];
355
356 _webStateListObservers.push_back(
Sylvain Defresne94394332018-01-22 13:11:00357 std::make_unique<WebStateListObserverBridge>(self));
358
359 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04360 std::make_unique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53361 tabModelClosingWebStateObserver));
362
Jinho Bangb940d6f42018-01-18 22:39:04363 _webStateListObservers.push_back(std::make_unique<TabParentingObserver>());
sdefresne3b0155e92017-04-12 15:18:53364
sdefresne4162a4dc2017-05-15 11:26:24365 TabModelSelectedTabObserver* tabModelSelectedTabObserver =
366 [[TabModelSelectedTabObserver alloc] initWithTabModel:self];
sdefresne3b0155e92017-04-12 15:18:53367 [retainedWebStateListObservers addObject:tabModelSelectedTabObserver];
sdefresneee210332017-03-24 10:36:54368 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04369 std::make_unique<WebStateListObserverBridge>(
sdefresne3b0155e92017-04-12 15:18:53370 tabModelSelectedTabObserver));
371
sdefresne4162a4dc2017-05-15 11:26:24372 TabModelObserversBridge* tabModelObserversBridge =
sdefresne3b0155e92017-04-12 15:18:53373 [[TabModelObserversBridge alloc] initWithTabModel:self
sdefresne4162a4dc2017-05-15 11:26:24374 tabModelObservers:_observers];
sdefresne3b0155e92017-04-12 15:18:53375 [retainedWebStateListObservers addObject:tabModelObserversBridge];
sdefresneee210332017-03-24 10:36:54376 _webStateListObservers.push_back(
Jinho Bangb940d6f42018-01-18 22:39:04377 std::make_unique<WebStateListObserverBridge>(tabModelObserversBridge));
sdefresne2f7781c2017-03-02 19:12:46378
Sylvain Defresne17b7b33f2017-11-10 19:55:15379 _webStateListObservers.push_back(
380 std::make_unique<TabModelFaviconDriverObserver>(self, _observers));
381
sdefresne2f7781c2017-03-02 19:12:46382 auto webStateListMetricsObserver =
Jinho Bangb940d6f42018-01-18 22:39:04383 std::make_unique<WebStateListMetricsObserver>();
sdefresne2f7781c2017-03-02 19:12:46384 _webStateListMetricsObserver = webStateListMetricsObserver.get();
sdefresneee210332017-03-24 10:36:54385 _webStateListObservers.push_back(std::move(webStateListMetricsObserver));
sdefresne41971892017-02-23 17:24:14386
Sylvain Defresnee8ae6ad2017-09-14 14:22:10387 auto tabModelNotificationObserver =
Jinho Bangb940d6f42018-01-18 22:39:04388 std::make_unique<TabModelNotificationObserver>(self);
Sylvain Defresnee8ae6ad2017-09-14 14:22:10389 _tabModelNotificationObserver = tabModelNotificationObserver.get();
390 _webStateListObservers.push_back(std::move(tabModelNotificationObserver));
Sylvain Defresne2b834542017-08-10 14:50:02391
sdefresneee210332017-03-24 10:36:54392 for (const auto& webStateListObserver : _webStateListObservers)
393 _webStateList->AddObserver(webStateListObserver.get());
sdefresne4162a4dc2017-05-15 11:26:24394 _retainedWebStateListObservers = [retainedWebStateListObservers copy];
sdefresne67dfdd62016-12-19 12:43:12395
sdefresne67dfdd62016-12-19 12:43:12396 // Register for resign active notification.
sdefresne44c09dc2017-02-07 11:13:08397 [[NSNotificationCenter defaultCenter]
398 addObserver:self
399 selector:@selector(willResignActive:)
400 name:UIApplicationWillResignActiveNotification
401 object:nil];
sdefresne67dfdd62016-12-19 12:43:12402 // Register for background notification.
sdefresne44c09dc2017-02-07 11:13:08403 [[NSNotificationCenter defaultCenter]
404 addObserver:self
405 selector:@selector(applicationDidEnterBackground:)
406 name:UIApplicationDidEnterBackgroundNotification
407 object:nil];
sdefresne8cfdf8f2017-01-11 18:22:14408
409 // Associate with ios::ChromeBrowserState.
Mohamad Ahmadie04d0952017-11-17 23:31:24410 TabModelList::RegisterTabModelWithChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12411 }
412 return self;
413}
414
sdefresne67dfdd62016-12-19 12:43:12415- (Tab*)tabAtIndex:(NSUInteger)index {
sdefresne32c0c382017-02-17 16:37:26416 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
417 return LegacyTabHelper::GetTabForWebState(
sdefresne365a73a2017-03-14 15:07:13418 _webStateList->GetWebStateAt(static_cast<int>(index)));
sdefresne67dfdd62016-12-19 12:43:12419}
420
421- (NSUInteger)indexOfTab:(Tab*)tab {
sdefresne365a73a2017-03-14 15:07:13422 int index = _webStateList->GetIndexOfWebState(tab.webState);
sdefresne32c0c382017-02-17 16:37:26423 if (index == WebStateList::kInvalidIndex)
424 return NSNotFound;
425
426 DCHECK_GE(index, 0);
427 return static_cast<NSUInteger>(index);
sdefresne67dfdd62016-12-19 12:43:12428}
429
sdefresnea6395912017-03-01 01:14:35430- (Tab*)insertTabWithURL:(const GURL&)URL
431 referrer:(const web::Referrer&)referrer
432 transition:(ui::PageTransition)transition
433 opener:(Tab*)parentTab
434 openedByDOM:(BOOL)openedByDOM
435 atIndex:(NSUInteger)index
436 inBackground:(BOOL)inBackground {
sdefresne67dfdd62016-12-19 12:43:12437 web::NavigationManager::WebLoadParams params(URL);
438 params.referrer = referrer;
439 params.transition_type = transition;
sdefresnea6395912017-03-01 01:14:35440 return [self insertTabWithLoadParams:params
441 opener:parentTab
442 openedByDOM:openedByDOM
443 atIndex:index
444 inBackground:inBackground];
sdefresne67dfdd62016-12-19 12:43:12445}
446
Eugene Butfcdd8112018-02-06 02:28:04447- (Tab*)insertOpenByDOMTabWithOpener:(Tab*)opener {
448 DCHECK(_browserState);
449 web::WebState::CreateParams createParams(_browserState);
450 createParams.created_with_opener = YES;
451 std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams);
452
453 int insertionFlags =
454 WebStateList::INSERT_FORCE_INDEX | WebStateList::INSERT_ACTIVATE;
455 int insertedIndex = _webStateList->InsertWebState(
456 _webStateList->count(), std::move(webState), insertionFlags,
457 WebStateOpener(opener.webState));
458
459 return LegacyTabHelper::GetTabForWebState(
460 _webStateList->GetWebStateAt(insertedIndex));
461}
462
sdefresnea6395912017-03-01 01:14:35463- (Tab*)insertTabWithLoadParams:
sdefresne2c600c52017-04-04 16:49:59464 (const web::NavigationManager::WebLoadParams&)loadParams
sdefresnea6395912017-03-01 01:14:35465 opener:(Tab*)parentTab
466 openedByDOM:(BOOL)openedByDOM
467 atIndex:(NSUInteger)index
468 inBackground:(BOOL)inBackground {
469 DCHECK(_browserState);
Mark Coganfb485c332018-11-13 10:37:07470 DCHECK(index == TabModelConstants::kTabPositionAutomatically ||
471 index <= self.count);
sdefresne67dfdd62016-12-19 12:43:12472
Sylvain Defresne419a7a72017-08-09 16:27:37473 int insertionIndex = WebStateList::kInvalidIndex;
474 int insertionFlags = WebStateList::INSERT_NO_FLAGS;
475 if (index != TabModelConstants::kTabPositionAutomatically) {
sdefresne2c600c52017-04-04 16:49:59476 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne419a7a72017-08-09 16:27:37477 insertionIndex = static_cast<int>(index);
478 insertionFlags |= WebStateList::INSERT_FORCE_INDEX;
479 } else if (!ui::PageTransitionCoreTypeIs(loadParams.transition_type,
480 ui::PAGE_TRANSITION_LINK)) {
481 insertionIndex = _webStateList->count();
482 insertionFlags |= WebStateList::INSERT_FORCE_INDEX;
sdefresne2c600c52017-04-04 16:49:59483 }
484
Sylvain Defresne2b834542017-08-10 14:50:02485 if (!inBackground) {
486 insertionFlags |= WebStateList::INSERT_ACTIVATE;
487 }
Sylvain Defresne419a7a72017-08-09 16:27:37488
Sylvain Defresne2b834542017-08-10 14:50:02489 web::WebState::CreateParams createParams(self.browserState);
490 createParams.created_with_opener = openedByDOM;
sdefresne2c600c52017-04-04 16:49:59491
Sylvain Defresne2b834542017-08-10 14:50:02492 std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams);
Sylvain Defresne419a7a72017-08-09 16:27:37493 webState->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresne2c600c52017-04-04 16:49:59494
Sylvain Defresne2b834542017-08-10 14:50:02495 insertionIndex = _webStateList->InsertWebState(
496 insertionIndex, std::move(webState), insertionFlags,
497 WebStateOpener(parentTab.webState));
sdefresne7d699dd2017-04-05 13:05:23498
Sylvain Defresne2b834542017-08-10 14:50:02499 return LegacyTabHelper::GetTabForWebState(
500 _webStateList->GetWebStateAt(insertionIndex));
sdefresne67dfdd62016-12-19 12:43:12501}
502
sdefresne67dfdd62016-12-19 12:43:12503- (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex {
sdefresne32c0c382017-02-17 16:37:26504 DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX));
sdefresne365a73a2017-03-14 15:07:13505 int fromIndex = _webStateList->GetIndexOfWebState(tab.webState);
506 _webStateList->MoveWebStateAt(fromIndex, static_cast<int>(toIndex));
sdefresne67dfdd62016-12-19 12:43:12507}
508
sdefresne67dfdd62016-12-19 12:43:12509- (void)closeTabAtIndex:(NSUInteger)index {
sdefresne2c600c52017-04-04 16:49:59510 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX));
Sylvain Defresne6b776d72017-10-18 21:14:06511 _webStateList->CloseWebStateAt(static_cast<int>(index),
512 WebStateList::CLOSE_USER_ACTION);
sdefresne67dfdd62016-12-19 12:43:12513}
514
515- (void)closeTab:(Tab*)tab {
sdefresne2c600c52017-04-04 16:49:59516 [self closeTabAtIndex:[self indexOfTab:tab]];
sdefresne67dfdd62016-12-19 12:43:12517}
518
519- (void)closeAllTabs {
Sylvain Defresne6b776d72017-10-18 21:14:06520 _webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION);
sdefresne67dfdd62016-12-19 12:43:12521}
522
523- (void)haltAllTabs {
Sylvain Defresne5b1174cb2018-01-16 15:47:58524 for (int index = 0; index < _webStateList->count(); ++index) {
525 web::WebState* webState = _webStateList->GetWebStateAt(index);
526 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
sdefresne67dfdd62016-12-19 12:43:12527 [tab terminateNetworkActivity];
528 }
529}
530
531- (void)notifyTabChanged:(Tab*)tab {
532 [_observers tabModel:self didChangeTab:tab];
533}
534
Mark Cogan5b494642018-01-02 12:55:46535- (void)notifyNewTabWillOpen:(Tab*)tab inBackground:(BOOL)background {
536 [_observers tabModel:self newTabWillOpen:tab inBackground:background];
537}
538
sdefresne67dfdd62016-12-19 12:43:12539- (void)addObserver:(id<TabModelObserver>)observer {
540 [_observers addObserver:observer];
541}
542
543- (void)removeObserver:(id<TabModelObserver>)observer {
544 [_observers removeObserver:observer];
545}
546
547- (void)resetSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46548 if (_webStateListMetricsObserver)
549 _webStateListMetricsObserver->ResetSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12550}
551
552- (void)recordSessionMetrics {
sdefresne2f7781c2017-03-02 19:12:46553 if (_webStateListMetricsObserver)
554 _webStateListMetricsObserver->RecordSessionMetrics();
sdefresne67dfdd62016-12-19 12:43:12555}
556
sdefresne67dfdd62016-12-19 12:43:12557- (void)setPrimary:(BOOL)primary {
Sylvain Defresne890975f52017-08-24 17:42:26558 if (_tabUsageRecorder) {
559 _tabUsageRecorder->RecordPrimaryTabModelChange(primary,
560 self.currentTab.webState);
561 }
sdefresne67dfdd62016-12-19 12:43:12562}
563
sdefresne67dfdd62016-12-19 12:43:12564// NOTE: This can be called multiple times, so must be robust against that.
565- (void)browserStateDestroyed {
sdefresne05315352017-05-23 14:46:17566 if (!_browserState)
567 return;
568
sdefresne67dfdd62016-12-19 12:43:12569 [[NSNotificationCenter defaultCenter] removeObserver:self];
Mohamad Ahmadie04d0952017-11-17 23:31:24570 TabModelList::UnregisterTabModelFromChromeBrowserState(_browserState, self);
sdefresne67dfdd62016-12-19 12:43:12571 _browserState = nullptr;
sdefresne05315352017-05-23 14:46:17572
Sylvain Defresnee8ae6ad2017-09-14 14:22:10573 // Clear weak pointer to observers before destroying them.
574 _tabModelNotificationObserver = nullptr;
sdefresne05315352017-05-23 14:46:17575 _webStateListMetricsObserver = nullptr;
576
577 // Close all tabs. Do this in an @autoreleasepool as WebStateList observers
578 // will be notified (they are unregistered later). As some of them may be
579 // implemented in Objective-C and unregister themselves in their -dealloc
580 // method, ensure they -autorelease introduced by ARC are processed before
581 // the WebStateList destructor is called.
582 @autoreleasepool {
Sylvain Defresne6b776d72017-10-18 21:14:06583 _webStateList->CloseAllWebStates(WebStateList::CLOSE_NO_FLAGS);
sdefresne05315352017-05-23 14:46:17584 }
585
586 // Unregister all observers after closing all the tabs as some of them are
587 // required to properly clean up the Tabs.
588 for (const auto& webStateListObserver : _webStateListObservers)
589 _webStateList->RemoveObserver(webStateListObserver.get());
590 _webStateListObservers.clear();
591 _retainedWebStateListObservers = nil;
592
593 _clearPoliciesTaskTracker.TryCancelAll();
Sylvain Defresne890975f52017-08-24 17:42:26594 _tabUsageRecorder.reset();
Sylvain Defresne94394332018-01-22 13:11:00595 _webStateObserver.reset();
sdefresne67dfdd62016-12-19 12:43:12596}
597
Mark Cogan30c24492019-01-16 16:52:58598#pragma mark - SessionWindowRestoring(public)
599
Mark Cogan30c24492019-01-16 16:52:58600- (void)saveSessionImmediately:(BOOL)immediately {
601 // Do nothing if there are tabs in the model but no selected tab. This is
602 // a transitional state.
603 if ((!self.currentTab && _webStateList->count()) || !_browserState)
604 return;
605 NSString* statePath =
606 base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe());
607 __weak TabModel* weakSelf = self;
608 SessionIOSFactory sessionFactory = ^{
609 return weakSelf.sessionForSaving;
610 };
611 [_sessionService saveSession:sessionFactory
612 directory:statePath
613 immediately:immediately];
614}
615
sdefresne67dfdd62016-12-19 12:43:12616#pragma mark - Private methods
617
sdefresnedcc6c6e2017-04-19 15:49:02618- (SessionIOS*)sessionForSaving {
sdefresne67dfdd62016-12-19 12:43:12619 // Build the array of sessions. Copy the session objects as the saving will
620 // be done on a separate thread.
621 // TODO(crbug.com/661986): This could get expensive especially since this
622 // window may never be saved (if another call comes in before the delay).
sdefresne68f546a2017-04-20 10:54:03623 return [[SessionIOS alloc]
624 initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]];
sdefresne67dfdd62016-12-19 12:43:12625}
626
Kurt Horimoto070e40b2018-08-31 20:50:00627- (BOOL)isWebUsageEnabled {
628 DCHECK(_browserState);
629 return WebStateListWebUsageEnablerFactory::GetInstance()
630 ->GetForBrowserState(_browserState)
631 ->IsWebUsageEnabled();
632}
633
sdefresne44c09dc2017-02-07 11:13:08634- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window
Rohit Rao0d9f6c3d2019-01-22 11:29:35635 forInitialRestore:(BOOL)initialRestore {
sdefresne44c09dc2017-02-07 11:13:08636 DCHECK(_browserState);
Rohit Rao0d9f6c3d2019-01-22 11:29:35637
638 // It is only ok to pass a nil |window| during the initial restore.
639 DCHECK(window || initialRestore);
640
641 // The initial restore can only happen before observers are registered.
642 DCHECK(!initialRestore || [_observers empty]);
sdefresne44c09dc2017-02-07 11:13:08643
Mark Cogan5b494642018-01-02 12:55:46644 // Disable calling -notifyNewTabWillOpen: while restoring a session as it
645 // breaks the BVC (see crbug.com/763964).
Sylvain Defresnee8ae6ad2017-09-14 14:22:10646 base::ScopedClosureRunner enableTabModelNotificationObserver;
647 if (_tabModelNotificationObserver) {
648 _tabModelNotificationObserver->SetDisabled(true);
649 enableTabModelNotificationObserver.ReplaceClosure(
650 base::BindOnce(&TabModelNotificationObserver::SetDisabled,
651 base::Unretained(_tabModelNotificationObserver), false));
652 }
653
sdefresne68f546a2017-04-20 10:54:03654 if (!window.sessions.count)
sdefresne44c09dc2017-02-07 11:13:08655 return NO;
656
sdefresne365a73a2017-03-14 15:07:13657 int oldCount = _webStateList->count();
sdefresne32c0c382017-02-17 16:37:26658 DCHECK_GE(oldCount, 0);
659
sdefresne08c0ab42017-04-05 12:49:43660 web::WebState::CreateParams createParams(_browserState);
661 DeserializeWebStateList(
sdefresnefd2aa652017-05-23 14:23:10662 _webStateList.get(), window,
Sylvain Defresne2b834542017-08-10 14:50:02663 base::BindRepeating(&web::WebState::CreateWithStorageSession,
664 createParams));
sdefresne08c0ab42017-04-05 12:49:43665
666 DCHECK_GT(_webStateList->count(), oldCount);
667 int restoredCount = _webStateList->count() - oldCount;
sdefresne68f546a2017-04-20 10:54:03668 DCHECK_EQ(window.sessions.count, static_cast<NSUInteger>(restoredCount));
sdefresne08c0ab42017-04-05 12:49:43669
sdefresnec92bada2017-02-08 14:43:49670 scoped_refptr<web::CertificatePolicyCache> policyCache =
671 web::BrowserState::GetCertificatePolicyCache(_browserState);
sdefresne44c09dc2017-02-07 11:13:08672
Sylvain Defresne890975f52017-08-24 17:42:26673 std::vector<web::WebState*> restoredWebStates;
674 if (_tabUsageRecorder)
675 restoredWebStates.reserve(window.sessions.count);
sdefresnee0a9ba62017-02-23 09:14:11676
sdefresne2c600c52017-04-04 16:49:59677 for (int index = oldCount; index < _webStateList->count(); ++index) {
678 web::WebState* webState = _webStateList->GetWebStateAt(index);
Sylvain Defresne7178d4c2017-09-14 13:22:37679 web::NavigationItem* visible_item =
680 webState->GetNavigationManager()->GetVisibleItem();
Gauthier Ambard32fe0dd2017-09-20 14:25:17681
682 if (!(visible_item &&
Sylvain Defresnec57fe762018-01-29 09:50:42683 visible_item->GetVirtualURL() == kChromeUINewTabURL)) {
Gauthier Ambard32fe0dd2017-09-20 14:25:17684 PagePlaceholderTabHelper::FromWebState(webState)
685 ->AddPlaceholderForNextNavigation();
686 }
687
Sylvain Defresne7178d4c2017-09-14 13:22:37688 if (visible_item && visible_item->GetVirtualURL().is_valid()) {
689 favicon::WebFaviconDriver::FromWebState(webState)->FetchFavicon(
690 visible_item->GetVirtualURL(), /*is_same_document=*/false);
691 }
692
sdefresne44c09dc2017-02-07 11:13:08693 // Restore the CertificatePolicyCache (note that webState is invalid after
sdefresnebf75f022017-03-07 17:48:35694 // passing it via move semantic to -initWithWebState:model:).
Eugene Butb56ecad2017-08-10 21:09:52695 UpdateCertificatePolicyCacheFromWebState(policyCache, webState);
Sylvain Defresne890975f52017-08-24 17:42:26696
697 if (_tabUsageRecorder)
698 restoredWebStates.push_back(webState);
sdefresne44c09dc2017-02-07 11:13:08699 }
sdefresnee0a9ba62017-02-23 09:14:11700
sdefresne44c09dc2017-02-07 11:13:08701 // If there was only one tab and it was the new tab page, clobber it.
702 BOOL closedNTPTab = NO;
703 if (oldCount == 1) {
sdefresne32c0c382017-02-17 16:37:26704 Tab* tab = [self tabAtIndex:0];
kkhorimoto2a192b82017-06-01 20:11:08705 BOOL hasPendingLoad =
706 tab.webState->GetNavigationManager()->GetPendingItem() != nullptr;
Sylvain Defresnee7f2c8a2017-10-17 02:39:19707 if (!hasPendingLoad &&
708 tab.webState->GetLastCommittedURL() == kChromeUINewTabURL) {
sdefresne44c09dc2017-02-07 11:13:08709 [self closeTab:tab];
710 closedNTPTab = YES;
711 oldCount = 0;
712 }
713 }
Sylvain Defresne890975f52017-08-24 17:42:26714 if (_tabUsageRecorder) {
715 _tabUsageRecorder->InitialRestoredTabs(self.currentTab.webState,
716 restoredWebStates);
717 }
Sylvain Defresnee8ae6ad2017-09-14 14:22:10718
Rohit Rao0d9f6c3d2019-01-22 11:29:35719 if (initialRestore) {
720 // Restore the session and reset the session metrics (as the event have
721 // not been generated by the user but by a cold start cycle).
722 [self resetSessionMetrics];
723 }
724
sdefresne44c09dc2017-02-07 11:13:08725 return closedNTPTab;
726}
727
sdefresne67dfdd62016-12-19 12:43:12728#pragma mark - Notification Handlers
729
730// Called when UIApplicationWillResignActiveNotification is received.
731- (void)willResignActive:(NSNotification*)notify {
Kurt Horimoto070e40b2018-08-31 20:50:00732 if (self.webUsageEnabled && self.currentTab) {
Sylvain Defresne528c17b2017-06-08 15:00:21733 [SnapshotCacheFactory::GetForBrowserState(_browserState)
sdefresne2f7781c2017-03-02 19:12:46734 willBeSavedGreyWhenBackgrounding:self.currentTab.tabId];
sdefresne67dfdd62016-12-19 12:43:12735 }
736}
737
738// Called when UIApplicationDidEnterBackgroundNotification is received.
739- (void)applicationDidEnterBackground:(NSNotification*)notify {
740 if (!_browserState)
741 return;
sdefresnec92bada2017-02-08 14:43:49742
sdefresne67dfdd62016-12-19 12:43:12743 // Evict all the certificate policies except for the current entries of the
744 // active sessions.
sdefresnec92bada2017-02-08 14:43:49745 CleanCertificatePolicyCache(
746 &_clearPoliciesTaskTracker,
Eric Seckler217006b2018-09-11 15:21:02747 base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}),
sdefresne32c0c382017-02-17 16:37:26748 web::BrowserState::GetCertificatePolicyCache(_browserState),
sdefresne365a73a2017-03-14 15:07:13749 _webStateList.get());
sdefresne67dfdd62016-12-19 12:43:12750
sdefresne67dfdd62016-12-19 12:43:12751 // Normally, the session is saved after some timer expires but since the app
752 // is about to enter the background send YES to save the session immediately.
753 [self saveSessionImmediately:YES];
754
755 // Write out a grey version of the current website to disk.
Kurt Horimoto070e40b2018-08-31 20:50:00756 if (self.webUsageEnabled && self.currentTab) {
Sylvain Defresne528c17b2017-06-08 15:00:21757 [SnapshotCacheFactory::GetForBrowserState(_browserState)
sdefresne2f7781c2017-03-02 19:12:46758 saveGreyInBackgroundForSessionID:self.currentTab.tabId];
sdefresne67dfdd62016-12-19 12:43:12759 }
760}
761
Sylvain Defresne94394332018-01-22 13:11:00762#pragma mark - CRWWebStateObserver
763
764- (void)webState:(web::WebState*)webState
Eugene But5c6a82d2019-01-10 17:15:32765 didFinishNavigation:(web::NavigationContext*)navigation {
Sylvain Defresne94394332018-01-22 13:11:00766 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
Rohit Raoeea69f72019-01-04 01:30:57767 [self notifyTabChanged:tab];
Sylvain Defresne94394332018-01-22 13:11:00768
Eugene But5c6a82d2019-01-10 17:15:32769 if (!navigation->IsSameDocument() && navigation->HasCommitted() &&
770 !self.offTheRecord) {
Eugene Butb117c322019-01-07 21:00:28771 int tabCount = static_cast<int>(self.count);
772 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tabCount, 1, 200, 50);
Sylvain Defresne94394332018-01-22 13:11:00773 }
774
Sylvain Defresne94394332018-01-22 13:11:00775 // Sending a notification about the url change for crash reporting.
776 // TODO(crbug.com/661675): Consider using the navigation entry committed
777 // notification now that it's in the right place.
778 const std::string& lastCommittedURL = webState->GetLastCommittedURL().spec();
779 if (!lastCommittedURL.empty()) {
780 [[NSNotificationCenter defaultCenter]
781 postNotificationName:kTabUrlStartedLoadingNotificationForCrashReporting
782 object:tab
783 userInfo:@{
784 kTabUrlKey : base::SysUTF8ToNSString(lastCommittedURL)
785 }];
786 }
787}
788
789- (void)webState:(web::WebState*)webState
790 didStartNavigation:(web::NavigationContext*)navigation {
791 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
792
793 // In order to avoid false positive in the crash loop detection, disable the
794 // counter as soon as an URL is loaded. This requires an user action and is a
795 // significant source of crashes. Ignore NTP as it is loaded by default after
796 // a crash.
797 if (navigation->GetUrl().host_piece() != kChromeUINewTabHost) {
798 static dispatch_once_t dispatch_once_token;
799 dispatch_once(&dispatch_once_token, ^{
800 crash_util::ResetFailedStartupAttemptCount();
801 });
802 }
803
804 if (_tabUsageRecorder && ShouldRecordPageLoadStartForNavigation(navigation)) {
805 _tabUsageRecorder->RecordPageLoadStart(webState);
806 }
807
808 [self notifyTabChanged:tab];
809 [[NSNotificationCenter defaultCenter]
810 postNotificationName:
811 kTabClosingCurrentDocumentNotificationForCrashReporting
812 object:tab];
813
814 DCHECK(webState->GetNavigationManager());
815 web::NavigationItem* navigationItem =
816 webState->GetNavigationManager()->GetPendingItem();
817
818 // TODO(crbug.com/676129): the pending item is not correctly set when the
819 // page is reloading, use the last committed item if pending item is null.
820 // Remove this once tracking bug is fixed.
821 if (!navigationItem) {
822 navigationItem = webState->GetNavigationManager()->GetLastCommittedItem();
823 }
824
Eugene But9c3e90c2019-01-04 20:45:04825 if (!navigationItem) {
826 // Pending item may not exist due to the bug in //ios/web layer.
827 // TODO(crbug.com/899827): remove this early return once GetPendingItem()
828 // always return valid object inside WebStateObserver::DidStartNavigation()
829 // callback.
830 //
831 // Note that GetLastCommittedItem() returns null if navigation manager does
832 // not have committed items (which is normal situation).
833 return;
834 }
835
Sylvain Defresne94394332018-01-22 13:11:00836 [[OmniboxGeolocationController sharedInstance]
837 addLocationToNavigationItem:navigationItem
838 browserState:ios::ChromeBrowserState::FromBrowserState(
839 webState->GetBrowserState())];
840}
841
Sylvain Defresne94394332018-01-22 13:11:00842- (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
843 DCHECK(!webState->IsLoading());
844 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
Rohit Raof3dde2b2019-01-14 14:23:15845 [self notifyTabChanged:tab];
Sylvain Defresne94394332018-01-22 13:11:00846
847 RecordInterfaceOrientationMetric();
848 RecordMainFrameNavigationMetric(webState);
849
mrefaatc977e372019-02-04 22:19:18850 [[OmniboxGeolocationController sharedInstance]
851 finishPageLoadForWebState:webState
852 loadSuccess:success];
Sylvain Defresne94394332018-01-22 13:11:00853}
854
855- (void)webState:(web::WebState*)webState
856 didChangeLoadingProgress:(double)progress {
857 // TODO(crbug.com/546406): It is probably possible to do something smarter,
858 // but the fact that this is not always sent will have to be taken into
859 // account.
860 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
861 [self notifyTabChanged:tab];
862}
863
864- (void)webStateDidChangeTitle:(web::WebState*)webState {
865 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
866 [self notifyTabChanged:tab];
867}
868
869- (void)webStateDidChangeVisibleSecurityState:(web::WebState*)webState {
870 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
871 [self notifyTabChanged:tab];
872}
873
874- (void)webStateDestroyed:(web::WebState*)webState {
875 // The TabModel is removed from WebState's observer when the WebState is
876 // detached from WebStateList which happens before WebState destructor,
877 // so this method should never be called.
878 NOTREACHED();
879}
880
881- (void)webStateDidStopLoading:(web::WebState*)webState {
882 Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
883 [self notifyTabChanged:tab];
884}
885
886#pragma mark - WebStateListObserving
887
888- (void)webStateList:(WebStateList*)webStateList
889 didInsertWebState:(web::WebState*)webState
890 atIndex:(int)index
891 activating:(BOOL)activating {
892 DCHECK(webState);
893 webState->AddObserver(_webStateObserver.get());
894}
895
896- (void)webStateList:(WebStateList*)webStateList
897 didReplaceWebState:(web::WebState*)oldWebState
898 withWebState:(web::WebState*)newWebState
899 atIndex:(int)atIndex {
900 DCHECK(oldWebState);
901 DCHECK(newWebState);
902 newWebState->AddObserver(_webStateObserver.get());
903 oldWebState->RemoveObserver(_webStateObserver.get());
904}
905
906- (void)webStateList:(WebStateList*)webStateList
907 didDetachWebState:(web::WebState*)webState
908 atIndex:(int)atIndex {
909 DCHECK(webState);
910 webState->RemoveObserver(_webStateObserver.get());
911}
912
sdefresne67dfdd62016-12-19 12:43:12913@end