sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #import "ios/chrome/browser/tabs/tab_model.h" |
| 6 | |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 7 | #include <cstdint> |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 8 | #include <utility> |
| 9 | #include <vector> |
| 10 | |
| 11 | #include "base/bind.h" |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 12 | #include "base/callback_helpers.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 13 | #include "base/logging.h" |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 14 | #import "base/mac/foundation_util.h" |
asvitkine | f1899e3 | 2017-01-27 16:30:29 | [diff] [blame] | 15 | #include "base/metrics/histogram_macros.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 16 | #include "base/metrics/user_metrics_action.h" |
| 17 | #include "base/strings/sys_string_conversions.h" |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 18 | #include "base/task/cancelable_task_tracker.h" |
Sylvain Defresne | 7178d4c | 2017-09-14 13:22:37 | [diff] [blame] | 19 | #include "components/favicon/ios/web_favicon_driver.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 20 | #include "components/navigation_metrics/navigation_metrics.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 21 | #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 Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 28 | #include "ios/chrome/browser/crash_loop_detection_util.h" |
| 29 | #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 30 | #import "ios/chrome/browser/metrics/tab_usage_recorder.h" |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 31 | #import "ios/chrome/browser/prerender/prerender_service_factory.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 32 | #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 33 | #import "ios/chrome/browser/sessions/session_ios.h" |
sdefresne | da974c8 | 2017-04-06 17:24:39 | [diff] [blame] | 34 | #import "ios/chrome/browser/sessions/session_service_ios.h" |
sdefresne | 5652815 | 2017-03-22 17:03:28 | [diff] [blame] | 35 | #import "ios/chrome/browser/sessions/session_window_ios.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 36 | #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 37 | #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h" |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 38 | #import "ios/chrome/browser/snapshots/snapshot_cache_web_state_list_observer.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 39 | #include "ios/chrome/browser/tab_parenting_global_observer.h" |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 40 | #import "ios/chrome/browser/tabs/legacy_tab_helper.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 41 | #import "ios/chrome/browser/tabs/tab.h" |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 42 | #import "ios/chrome/browser/tabs/tab_model_closing_web_state_observer.h" |
Sylvain Defresne | 17b7b33f | 2017-11-10 19:55:15 | [diff] [blame] | 43 | #import "ios/chrome/browser/tabs/tab_model_favicon_driver_observer.h" |
sdefresne | 8cfdf8f | 2017-01-11 18:22:14 | [diff] [blame] | 44 | #import "ios/chrome/browser/tabs/tab_model_list.h" |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 45 | #import "ios/chrome/browser/tabs/tab_model_notification_observer.h" |
sdefresne | 0a762e4 | 2017-02-14 11:11:25 | [diff] [blame] | 46 | #import "ios/chrome/browser/tabs/tab_model_observers.h" |
sdefresne | 39bfaab | 2017-02-20 11:06:36 | [diff] [blame] | 47 | #import "ios/chrome/browser/tabs/tab_model_observers_bridge.h" |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 48 | #import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 49 | #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 50 | #import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.h" |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 51 | #import "ios/chrome/browser/tabs/tab_model_web_usage_enabled_observer.h" |
sdefresne | 39bfaab | 2017-02-20 11:06:36 | [diff] [blame] | 52 | #import "ios/chrome/browser/tabs/tab_parenting_observer.h" |
Eugene But | b56ecad | 2017-08-10 21:09:52 | [diff] [blame] | 53 | #import "ios/chrome/browser/web/page_placeholder_tab_helper.h" |
sdefresne | 62a00bb | 2017-04-10 15:36:05 | [diff] [blame] | 54 | #import "ios/chrome/browser/web_state_list/web_state_list.h" |
sdefresne | 62a00bb | 2017-04-10 15:36:05 | [diff] [blame] | 55 | #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" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 59 | #include "ios/web/public/browser_state.h" |
| 60 | #include "ios/web/public/certificate_policy_cache.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 61 | #import "ios/web/public/load_committed_details.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 62 | #include "ios/web/public/navigation_item.h" |
| 63 | #import "ios/web/public/navigation_manager.h" |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 64 | #import "ios/web/public/serializable_user_data_manager.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 65 | #import "ios/web/public/web_state/navigation_context.h" |
kkhorimoto | d3a3986f | 2017-04-05 02:21:41 | [diff] [blame] | 66 | #include "ios/web/public/web_state/session_certificate_policy_cache.h" |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 67 | #import "ios/web/public/web_state/web_state_observer_bridge.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 68 | #include "ios/web/public/web_thread.h" |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 69 | #include "url/gurl.h" |
| 70 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 71 | #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 72 | #error "This file requires ARC support." |
| 73 | #endif |
| 74 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 75 | namespace { |
| 76 | |
| 77 | // Updates CRWSessionCertificatePolicyManager's certificate policy cache. |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 78 | void UpdateCertificatePolicyCacheFromWebState( |
| 79 | const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
kkhorimoto | d3a3986f | 2017-04-05 02:21:41 | [diff] [blame] | 80 | const web::WebState* web_state) { |
sdefresne | 4c8bf63 | 2017-01-26 15:46:47 | [diff] [blame] | 81 | DCHECK(web_state); |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 82 | DCHECK_CURRENTLY_ON(web::WebThread::UI); |
kkhorimoto | d3a3986f | 2017-04-05 02:21:41 | [diff] [blame] | 83 | web_state->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache( |
| 84 | policy_cache); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 85 | } |
| 86 | |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 87 | // Populates the certificate policy cache based on the WebStates of |
| 88 | // |web_state_list|. |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 89 | void RestoreCertificatePolicyCacheFromModel( |
| 90 | const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 91 | WebStateList* web_state_list) { |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 92 | DCHECK_CURRENTLY_ON(web::WebThread::UI); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 93 | for (int index = 0; index < web_state_list->count(); ++index) { |
| 94 | UpdateCertificatePolicyCacheFromWebState( |
| 95 | policy_cache, web_state_list->GetWebStateAt(index)); |
| 96 | } |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 97 | } |
| 98 | |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 99 | // Scrubs the certificate policy cache of all certificates policies except |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 100 | // those for the current entries in |web_state_list|. |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 101 | void CleanCertificatePolicyCache( |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 102 | base::CancelableTaskTracker* task_tracker, |
| 103 | const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 104 | const scoped_refptr<web::CertificatePolicyCache>& policy_cache, |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 105 | WebStateList* web_state_list) { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 106 | DCHECK(policy_cache); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 107 | DCHECK(web_state_list); |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 108 | 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, |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 114 | base::Unretained(web_state_list))); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 115 | } |
| 116 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 117 | // Returns whether |rhs| and |lhs| are different user agent types. If either |
| 118 | // of them is web::UserAgentType::NONE, then return NO. |
| 119 | BOOL 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. |
| 132 | BOOL 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. |
| 187 | void 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. |
| 205 | void 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 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 216 | } // anonymous namespace |
| 217 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 218 | @interface TabModel ()<CRWWebStateObserver, WebStateListObserving> { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 219 | // Delegate for the WebStateList. |
| 220 | std::unique_ptr<WebStateListDelegate> _webStateListDelegate; |
| 221 | |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 222 | // Underlying shared model implementation. |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 223 | std::unique_ptr<WebStateList> _webStateList; |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 224 | |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 225 | // 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; |
sdefresne | 39bfaab | 2017-02-20 11:06:36 | [diff] [blame] | 228 | |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 229 | // Strong references to id<WebStateListObserving> wrapped by non-owning |
| 230 | // WebStateListObserverBridges. |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 231 | NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 232 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 233 | // The delegate for sync. |
| 234 | std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 235 | |
Mark Cogan | 5b49464 | 2018-01-02 12:55:46 | [diff] [blame] | 236 | // The observer that calls -notifyNewTabWillOpen on this object. |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 237 | TabModelNotificationObserver* _tabModelNotificationObserver; |
| 238 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 239 | // Counters for metrics. |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 240 | WebStateListMetricsObserver* _webStateListMetricsObserver; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 241 | |
| 242 | // Backs up property with the same name. |
| 243 | std::unique_ptr<TabUsageRecorder> _tabUsageRecorder; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 244 | // Saves session's state. |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 245 | SessionServiceIOS* _sessionService; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 246 | // List of TabModelObservers. |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 247 | TabModelObservers* _observers; |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 248 | |
| 249 | // Used to ensure thread-safety of the certificate policy management code. |
| 250 | base::CancelableTaskTracker _clearPoliciesTaskTracker; |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 251 | |
| 252 | // Used to observe owned Tabs' WebStates. |
| 253 | std::unique_ptr<web::WebStateObserver> _webStateObserver; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | // Session window for the contents of the tab model. |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 257 | @property(nonatomic, readonly) SessionIOS* sessionForSaving; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 258 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 259 | // 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 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 265 | @end |
| 266 | |
| 267 | @implementation TabModel |
| 268 | |
| 269 | @synthesize browserState = _browserState; |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 270 | @synthesize webUsageEnabled = _webUsageEnabled; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 271 | |
| 272 | #pragma mark - Overriden |
| 273 | |
| 274 | - (void)dealloc { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 275 | // browserStateDestroyed should always have been called before destruction. |
| 276 | DCHECK(!_browserState); |
| 277 | |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 278 | // Make sure the observers do clean after themselves. |
| 279 | DCHECK([_observers empty]); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 280 | } |
| 281 | |
| 282 | #pragma mark - Public methods |
| 283 | |
| 284 | - (Tab*)currentTab { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 285 | web::WebState* webState = _webStateList->GetActiveWebState(); |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 286 | return webState ? LegacyTabHelper::GetTabForWebState(webState) : nil; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | - (void)setCurrentTab:(Tab*)newTab { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 290 | int indexOfTab = _webStateList->GetIndexOfWebState(newTab.webState); |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 291 | DCHECK_NE(indexOfTab, WebStateList::kInvalidIndex); |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 292 | _webStateList->ActivateWebStateAt(indexOfTab); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 293 | } |
| 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 { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 308 | return _webStateList->empty(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 309 | } |
| 310 | |
sdefresne | 297553e5 | 2017-01-25 17:09:37 | [diff] [blame] | 311 | - (NSUInteger)count { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 312 | DCHECK_GE(_webStateList->count(), 0); |
| 313 | return static_cast<NSUInteger>(_webStateList->count()); |
sdefresne | 297553e5 | 2017-01-25 17:09:37 | [diff] [blame] | 314 | } |
| 315 | |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 316 | - (WebStateList*)webStateList { |
| 317 | return _webStateList.get(); |
| 318 | } |
| 319 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 320 | - (instancetype)initWithSessionWindow:(SessionWindowIOS*)window |
| 321 | sessionService:(SessionServiceIOS*)service |
| 322 | browserState:(ios::ChromeBrowserState*)browserState { |
| 323 | if ((self = [super init])) { |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 324 | _observers = [TabModelObservers observers]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 325 | |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 326 | _webStateListDelegate = |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 327 | std::make_unique<TabModelWebStateListDelegate>(self); |
| 328 | _webStateList = std::make_unique<WebStateList>(_webStateListDelegate.get()); |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 329 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 330 | _browserState = browserState; |
| 331 | DCHECK(_browserState); |
| 332 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 333 | _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self); |
| 334 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 335 | // 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 Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 340 | _tabUsageRecorder = std::make_unique<TabUsageRecorder>( |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 341 | _webStateList.get(), |
| 342 | PrerenderServiceFactory::GetForBrowserState(browserState)); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 343 | } |
Sylvain Defresne | 72c1e99 | 2018-01-15 14:26:17 | [diff] [blame] | 344 | _syncedWindowDelegate = |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 345 | std::make_unique<TabModelSyncedWindowDelegate>(_webStateList.get()); |
sdefresne | 4197189 | 2017-02-23 17:24:14 | [diff] [blame] | 346 | |
| 347 | // There must be a valid session service defined to consume session windows. |
| 348 | DCHECK(service); |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 349 | _sessionService = service; |
sdefresne | 4197189 | 2017-02-23 17:24:14 | [diff] [blame] | 350 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 351 | NSMutableArray<id<WebStateListObserving>>* retainedWebStateListObservers = |
| 352 | [[NSMutableArray alloc] init]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 353 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 354 | TabModelClosingWebStateObserver* tabModelClosingWebStateObserver = [ |
| 355 | [TabModelClosingWebStateObserver alloc] |
| 356 | initWithTabModel:self |
| 357 | restoreService:IOSChromeTabRestoreServiceFactory::GetForBrowserState( |
| 358 | _browserState)]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 359 | [retainedWebStateListObservers addObject:tabModelClosingWebStateObserver]; |
| 360 | |
| 361 | _webStateListObservers.push_back( |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 362 | std::make_unique<WebStateListObserverBridge>(self)); |
| 363 | |
| 364 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 365 | std::make_unique<WebStateListObserverBridge>( |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 366 | tabModelClosingWebStateObserver)); |
| 367 | |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 368 | SnapshotCache* snapshotCache = |
| 369 | SnapshotCacheFactory::GetForBrowserState(_browserState); |
| 370 | if (snapshotCache) { |
| 371 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 372 | std::make_unique<SnapshotCacheWebStateListObserver>(snapshotCache)); |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 373 | } |
| 374 | |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 375 | _webStateListObservers.push_back(std::make_unique<TabParentingObserver>()); |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 376 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 377 | TabModelSelectedTabObserver* tabModelSelectedTabObserver = |
| 378 | [[TabModelSelectedTabObserver alloc] initWithTabModel:self]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 379 | [retainedWebStateListObservers addObject:tabModelSelectedTabObserver]; |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 380 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 381 | std::make_unique<WebStateListObserverBridge>( |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 382 | tabModelSelectedTabObserver)); |
| 383 | |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 384 | TabModelObserversBridge* tabModelObserversBridge = |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 385 | [[TabModelObserversBridge alloc] initWithTabModel:self |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 386 | tabModelObservers:_observers]; |
sdefresne | 3b0155e9 | 2017-04-12 15:18:53 | [diff] [blame] | 387 | [retainedWebStateListObservers addObject:tabModelObserversBridge]; |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 388 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 389 | std::make_unique<WebStateListObserverBridge>(tabModelObserversBridge)); |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 390 | |
Sylvain Defresne | 17b7b33f | 2017-11-10 19:55:15 | [diff] [blame] | 391 | _webStateListObservers.push_back( |
| 392 | std::make_unique<TabModelFaviconDriverObserver>(self, _observers)); |
| 393 | |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 394 | auto webStateListMetricsObserver = |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 395 | std::make_unique<WebStateListMetricsObserver>(); |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 396 | _webStateListMetricsObserver = webStateListMetricsObserver.get(); |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 397 | _webStateListObservers.push_back(std::move(webStateListMetricsObserver)); |
sdefresne | 4197189 | 2017-02-23 17:24:14 | [diff] [blame] | 398 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 399 | _webStateListObservers.push_back( |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 400 | std::make_unique<TabModelWebUsageEnabledObserver>(self)); |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 401 | |
| 402 | auto tabModelNotificationObserver = |
Jinho Bang | b940d6f4 | 2018-01-18 22:39:04 | [diff] [blame] | 403 | std::make_unique<TabModelNotificationObserver>(self); |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 404 | _tabModelNotificationObserver = tabModelNotificationObserver.get(); |
| 405 | _webStateListObservers.push_back(std::move(tabModelNotificationObserver)); |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 406 | |
sdefresne | ee21033 | 2017-03-24 10:36:54 | [diff] [blame] | 407 | for (const auto& webStateListObserver : _webStateListObservers) |
| 408 | _webStateList->AddObserver(webStateListObserver.get()); |
sdefresne | 4162a4dc | 2017-05-15 11:26:24 | [diff] [blame] | 409 | _retainedWebStateListObservers = [retainedWebStateListObservers copy]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 410 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 411 | if (window) { |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 412 | 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]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 417 | } |
| 418 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 419 | // Register for resign active notification. |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 420 | [[NSNotificationCenter defaultCenter] |
| 421 | addObserver:self |
| 422 | selector:@selector(willResignActive:) |
| 423 | name:UIApplicationWillResignActiveNotification |
| 424 | object:nil]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 425 | // Register for background notification. |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 426 | [[NSNotificationCenter defaultCenter] |
| 427 | addObserver:self |
| 428 | selector:@selector(applicationDidEnterBackground:) |
| 429 | name:UIApplicationDidEnterBackgroundNotification |
| 430 | object:nil]; |
sdefresne | 8cfdf8f | 2017-01-11 18:22:14 | [diff] [blame] | 431 | |
| 432 | // Associate with ios::ChromeBrowserState. |
Mohamad Ahmadi | e04d095 | 2017-11-17 23:31:24 | [diff] [blame] | 433 | TabModelList::RegisterTabModelWithChromeBrowserState(_browserState, self); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 434 | } |
| 435 | return self; |
| 436 | } |
| 437 | |
| 438 | - (instancetype)init { |
| 439 | NOTREACHED(); |
| 440 | return nil; |
| 441 | } |
| 442 | |
| 443 | - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window { |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 444 | return [self restoreSessionWindow:window persistState:YES]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 445 | } |
| 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. |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 450 | if ((!self.currentTab && _webStateList->count()) || !_browserState) |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 451 | return; |
sdefresne | 80a54462 | 2017-04-12 12:44:27 | [diff] [blame] | 452 | NSString* statePath = |
| 453 | base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe()); |
Sylvain Defresne | 5fbebad | 2017-06-01 11:23:53 | [diff] [blame] | 454 | __weak TabModel* weakSelf = self; |
| 455 | SessionIOSFactory sessionFactory = ^{ |
| 456 | return weakSelf.sessionForSaving; |
| 457 | }; |
| 458 | [_sessionService saveSession:sessionFactory |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 459 | directory:statePath |
| 460 | immediately:immediately]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 461 | } |
| 462 | |
| 463 | - (Tab*)tabAtIndex:(NSUInteger)index { |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 464 | DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
| 465 | return LegacyTabHelper::GetTabForWebState( |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 466 | _webStateList->GetWebStateAt(static_cast<int>(index))); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 467 | } |
| 468 | |
| 469 | - (NSUInteger)indexOfTab:(Tab*)tab { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 470 | int index = _webStateList->GetIndexOfWebState(tab.webState); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 471 | if (index == WebStateList::kInvalidIndex) |
| 472 | return NSNotFound; |
| 473 | |
| 474 | DCHECK_GE(index, 0); |
| 475 | return static_cast<NSUInteger>(index); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 476 | } |
| 477 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 478 | - (Tab*)openerOfTab:(Tab*)tab { |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 479 | int index = _webStateList->GetIndexOfWebState(tab.webState); |
sdefresne | e0a9ba6 | 2017-02-23 09:14:11 | [diff] [blame] | 480 | if (index == WebStateList::kInvalidIndex) |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 481 | return nil; |
sdefresne | e0a9ba6 | 2017-02-23 09:14:11 | [diff] [blame] | 482 | |
sdefresne | 500f522 | 2017-03-24 14:01:40 | [diff] [blame] | 483 | WebStateOpener opener = _webStateList->GetOpenerOfWebStateAt(index); |
| 484 | return opener.opener ? LegacyTabHelper::GetTabForWebState(opener.opener) |
| 485 | : nil; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 486 | } |
| 487 | |
sdefresne | a639591 | 2017-03-01 01:14:35 | [diff] [blame] | 488 | - (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 { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 495 | web::NavigationManager::WebLoadParams params(URL); |
| 496 | params.referrer = referrer; |
| 497 | params.transition_type = transition; |
sdefresne | a639591 | 2017-03-01 01:14:35 | [diff] [blame] | 498 | return [self insertTabWithLoadParams:params |
| 499 | opener:parentTab |
| 500 | openedByDOM:openedByDOM |
| 501 | atIndex:index |
| 502 | inBackground:inBackground]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 503 | } |
| 504 | |
sdefresne | a639591 | 2017-03-01 01:14:35 | [diff] [blame] | 505 | - (Tab*)insertTabWithLoadParams: |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 506 | (const web::NavigationManager::WebLoadParams&)loadParams |
sdefresne | a639591 | 2017-03-01 01:14:35 | [diff] [blame] | 507 | opener:(Tab*)parentTab |
| 508 | openedByDOM:(BOOL)openedByDOM |
| 509 | atIndex:(NSUInteger)index |
| 510 | inBackground:(BOOL)inBackground { |
| 511 | DCHECK(_browserState); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 512 | |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 513 | int insertionIndex = WebStateList::kInvalidIndex; |
| 514 | int insertionFlags = WebStateList::INSERT_NO_FLAGS; |
| 515 | if (index != TabModelConstants::kTabPositionAutomatically) { |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 516 | DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 517 | 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; |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 523 | } |
| 524 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 525 | if (!inBackground) { |
| 526 | insertionFlags |= WebStateList::INSERT_ACTIVATE; |
| 527 | } |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 528 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 529 | web::WebState::CreateParams createParams(self.browserState); |
| 530 | createParams.created_with_opener = openedByDOM; |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 531 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 532 | std::unique_ptr<web::WebState> webState = web::WebState::Create(createParams); |
Sylvain Defresne | 419a7a7 | 2017-08-09 16:27:37 | [diff] [blame] | 533 | webState->GetNavigationManager()->LoadURLWithParams(loadParams); |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 534 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 535 | insertionIndex = _webStateList->InsertWebState( |
| 536 | insertionIndex, std::move(webState), insertionFlags, |
| 537 | WebStateOpener(parentTab.webState)); |
sdefresne | 7d699dd | 2017-04-05 13:05:23 | [diff] [blame] | 538 | |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 539 | return LegacyTabHelper::GetTabForWebState( |
| 540 | _webStateList->GetWebStateAt(insertionIndex)); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 541 | } |
| 542 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 543 | - (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex { |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 544 | DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX)); |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 545 | int fromIndex = _webStateList->GetIndexOfWebState(tab.webState); |
| 546 | _webStateList->MoveWebStateAt(fromIndex, static_cast<int>(toIndex)); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 547 | } |
| 548 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 549 | - (void)closeTabAtIndex:(NSUInteger)index { |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 550 | DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
Sylvain Defresne | 6b776d7 | 2017-10-18 21:14:06 | [diff] [blame] | 551 | _webStateList->CloseWebStateAt(static_cast<int>(index), |
| 552 | WebStateList::CLOSE_USER_ACTION); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 553 | } |
| 554 | |
| 555 | - (void)closeTab:(Tab*)tab { |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 556 | [self closeTabAtIndex:[self indexOfTab:tab]]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 557 | } |
| 558 | |
| 559 | - (void)closeAllTabs { |
Sylvain Defresne | 6b776d7 | 2017-10-18 21:14:06 | [diff] [blame] | 560 | _webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION); |
Mark Cogan | 3c052431 | 2018-01-02 16:00:45 | [diff] [blame] | 561 | [_observers tabModelClosedAllTabs:self]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 562 | } |
| 563 | |
| 564 | - (void)haltAllTabs { |
Sylvain Defresne | 5b1174cb | 2018-01-16 15:47:58 | [diff] [blame] | 565 | for (int index = 0; index < _webStateList->count(); ++index) { |
| 566 | web::WebState* webState = _webStateList->GetWebStateAt(index); |
| 567 | Tab* tab = LegacyTabHelper::GetTabForWebState(webState); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 568 | [tab terminateNetworkActivity]; |
| 569 | } |
| 570 | } |
| 571 | |
| 572 | - (void)notifyTabChanged:(Tab*)tab { |
| 573 | [_observers tabModel:self didChangeTab:tab]; |
| 574 | } |
| 575 | |
Mark Cogan | 5b49464 | 2018-01-02 12:55:46 | [diff] [blame] | 576 | - (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 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 595 | - (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 { |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 604 | if (_webStateListMetricsObserver) |
| 605 | _webStateListMetricsObserver->ResetSessionMetrics(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 606 | } |
| 607 | |
| 608 | - (void)recordSessionMetrics { |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 609 | if (_webStateListMetricsObserver) |
| 610 | _webStateListMetricsObserver->RecordSessionMetrics(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 611 | } |
| 612 | |
| 613 | - (void)notifyTabSnapshotChanged:(Tab*)tab withImage:(UIImage*)image { |
| 614 | DCHECK([NSThread isMainThread]); |
| 615 | [_observers tabModel:self didChangeTabSnapshot:tab withImage:image]; |
| 616 | } |
| 617 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 618 | - (void)setWebUsageEnabled:(BOOL)webUsageEnabled { |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 619 | if (_webUsageEnabled == webUsageEnabled) |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 620 | return; |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 621 | _webUsageEnabled = webUsageEnabled; |
sdefresne | 7d699dd | 2017-04-05 13:05:23 | [diff] [blame] | 622 | for (int index = 0; index < _webStateList->count(); ++index) { |
| 623 | web::WebState* webState = _webStateList->GetWebStateAt(index); |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 624 | webState->SetWebUsageEnabled(_webUsageEnabled ? true : false); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 625 | } |
| 626 | } |
| 627 | |
| 628 | - (void)setPrimary:(BOOL)primary { |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 629 | if (_tabUsageRecorder) { |
| 630 | _tabUsageRecorder->RecordPrimaryTabModelChange(primary, |
| 631 | self.currentTab.webState); |
| 632 | } |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 633 | } |
| 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 Defresne | 5b1174cb | 2018-01-16 15:47:58 | [diff] [blame] | 640 | for (int index = 0; index < _webStateList->count(); ++index) { |
| 641 | web::WebState* webState = _webStateList->GetWebStateAt(index); |
| 642 | const GURL& lastCommittedURL = webState->GetLastCommittedURL(); |
kkhorimoto | 2a192b8 | 2017-06-01 20:11:08 | [diff] [blame] | 643 | if (UrlIsExternalFileReference(lastCommittedURL)) { |
| 644 | [referencedFiles addObject:base::SysUTF8ToNSString( |
| 645 | lastCommittedURL.ExtractFileName())]; |
| 646 | } |
| 647 | web::NavigationItem* pendingItem = |
Sylvain Defresne | 5b1174cb | 2018-01-16 15:47:58 | [diff] [blame] | 648 | webState->GetNavigationManager()->GetPendingItem(); |
kkhorimoto | 2a192b8 | 2017-06-01 20:11:08 | [diff] [blame] | 649 | if (pendingItem && UrlIsExternalFileReference(pendingItem->GetURL())) { |
| 650 | [referencedFiles addObject:base::SysUTF8ToNSString( |
| 651 | pendingItem->GetURL().ExtractFileName())]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 652 | } |
| 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 { |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 675 | if (!_browserState) |
| 676 | return; |
| 677 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 678 | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
Mohamad Ahmadi | e04d095 | 2017-11-17 23:31:24 | [diff] [blame] | 679 | TabModelList::UnregisterTabModelFromChromeBrowserState(_browserState, self); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 680 | _browserState = nullptr; |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 681 | |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 682 | // Clear weak pointer to observers before destroying them. |
| 683 | _tabModelNotificationObserver = nullptr; |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 684 | _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 Defresne | 6b776d7 | 2017-10-18 21:14:06 | [diff] [blame] | 692 | _webStateList->CloseAllWebStates(WebStateList::CLOSE_NO_FLAGS); |
sdefresne | 0531535 | 2017-05-23 14:46:17 | [diff] [blame] | 693 | } |
| 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 Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 703 | _tabUsageRecorder.reset(); |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 704 | _webStateObserver.reset(); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 705 | } |
| 706 | |
sdefresne | 573bd7b | 2017-03-03 00:12:49 | [diff] [blame] | 707 | - (void)navigationCommittedInTab:(Tab*)tab |
| 708 | previousItem:(web::NavigationItem*)previousItem { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 709 | 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. |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 715 | 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 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 731 | #pragma mark - Private methods |
| 732 | |
sdefresne | dcc6c6e | 2017-04-19 15:49:02 | [diff] [blame] | 733 | - (SessionIOS*)sessionForSaving { |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 734 | // 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). |
sdefresne | 68f546a | 2017-04-20 10:54:03 | [diff] [blame] | 738 | return [[SessionIOS alloc] |
| 739 | initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 740 | } |
| 741 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 742 | - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window |
| 743 | persistState:(BOOL)persistState { |
| 744 | DCHECK(_browserState); |
| 745 | DCHECK(window); |
| 746 | |
Mark Cogan | 5b49464 | 2018-01-02 12:55:46 | [diff] [blame] | 747 | // Disable calling -notifyNewTabWillOpen: while restoring a session as it |
| 748 | // breaks the BVC (see crbug.com/763964). |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 749 | 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 | |
sdefresne | 68f546a | 2017-04-20 10:54:03 | [diff] [blame] | 757 | if (!window.sessions.count) |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 758 | return NO; |
| 759 | |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 760 | int oldCount = _webStateList->count(); |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 761 | DCHECK_GE(oldCount, 0); |
| 762 | |
sdefresne | 08c0ab4 | 2017-04-05 12:49:43 | [diff] [blame] | 763 | web::WebState::CreateParams createParams(_browserState); |
| 764 | DeserializeWebStateList( |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 765 | _webStateList.get(), window, |
Sylvain Defresne | 2b83454 | 2017-08-10 14:50:02 | [diff] [blame] | 766 | base::BindRepeating(&web::WebState::CreateWithStorageSession, |
| 767 | createParams)); |
sdefresne | 08c0ab4 | 2017-04-05 12:49:43 | [diff] [blame] | 768 | |
| 769 | DCHECK_GT(_webStateList->count(), oldCount); |
| 770 | int restoredCount = _webStateList->count() - oldCount; |
sdefresne | 68f546a | 2017-04-20 10:54:03 | [diff] [blame] | 771 | DCHECK_EQ(window.sessions.count, static_cast<NSUInteger>(restoredCount)); |
sdefresne | 08c0ab4 | 2017-04-05 12:49:43 | [diff] [blame] | 772 | |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 773 | scoped_refptr<web::CertificatePolicyCache> policyCache = |
| 774 | web::BrowserState::GetCertificatePolicyCache(_browserState); |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 775 | |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 776 | std::vector<web::WebState*> restoredWebStates; |
| 777 | if (_tabUsageRecorder) |
| 778 | restoredWebStates.reserve(window.sessions.count); |
sdefresne | e0a9ba6 | 2017-02-23 09:14:11 | [diff] [blame] | 779 | |
sdefresne | 2c600c5 | 2017-04-04 16:49:59 | [diff] [blame] | 780 | for (int index = oldCount; index < _webStateList->count(); ++index) { |
| 781 | web::WebState* webState = _webStateList->GetWebStateAt(index); |
Sylvain Defresne | 7178d4c | 2017-09-14 13:22:37 | [diff] [blame] | 782 | web::NavigationItem* visible_item = |
| 783 | webState->GetNavigationManager()->GetVisibleItem(); |
Gauthier Ambard | 32fe0dd | 2017-09-20 14:25:17 | [diff] [blame] | 784 | |
| 785 | if (!(visible_item && |
| 786 | visible_item->GetVirtualURL() == GURL(kChromeUINewTabURL))) { |
| 787 | PagePlaceholderTabHelper::FromWebState(webState) |
| 788 | ->AddPlaceholderForNextNavigation(); |
| 789 | } |
| 790 | |
Sylvain Defresne | 7178d4c | 2017-09-14 13:22:37 | [diff] [blame] | 791 | if (visible_item && visible_item->GetVirtualURL().is_valid()) { |
| 792 | favicon::WebFaviconDriver::FromWebState(webState)->FetchFavicon( |
| 793 | visible_item->GetVirtualURL(), /*is_same_document=*/false); |
| 794 | } |
| 795 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 796 | // Restore the CertificatePolicyCache (note that webState is invalid after |
sdefresne | bf75f02 | 2017-03-07 17:48:35 | [diff] [blame] | 797 | // passing it via move semantic to -initWithWebState:model:). |
Eugene But | b56ecad | 2017-08-10 21:09:52 | [diff] [blame] | 798 | UpdateCertificatePolicyCacheFromWebState(policyCache, webState); |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 799 | |
| 800 | if (_tabUsageRecorder) |
| 801 | restoredWebStates.push_back(webState); |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 802 | } |
sdefresne | e0a9ba6 | 2017-02-23 09:14:11 | [diff] [blame] | 803 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 804 | // If there was only one tab and it was the new tab page, clobber it. |
| 805 | BOOL closedNTPTab = NO; |
| 806 | if (oldCount == 1) { |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 807 | Tab* tab = [self tabAtIndex:0]; |
kkhorimoto | 2a192b8 | 2017-06-01 20:11:08 | [diff] [blame] | 808 | BOOL hasPendingLoad = |
| 809 | tab.webState->GetNavigationManager()->GetPendingItem() != nullptr; |
Sylvain Defresne | e7f2c8a | 2017-10-17 02:39:19 | [diff] [blame] | 810 | if (!hasPendingLoad && |
| 811 | tab.webState->GetLastCommittedURL() == kChromeUINewTabURL) { |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 812 | [self closeTab:tab]; |
| 813 | closedNTPTab = YES; |
| 814 | oldCount = 0; |
| 815 | } |
| 816 | } |
Sylvain Defresne | 890975f5 | 2017-08-24 17:42:26 | [diff] [blame] | 817 | if (_tabUsageRecorder) { |
| 818 | _tabUsageRecorder->InitialRestoredTabs(self.currentTab.webState, |
| 819 | restoredWebStates); |
| 820 | } |
Sylvain Defresne | e8ae6ad | 2017-09-14 14:22:10 | [diff] [blame] | 821 | |
sdefresne | 44c09dc | 2017-02-07 11:13:08 | [diff] [blame] | 822 | return closedNTPTab; |
| 823 | } |
| 824 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 825 | #pragma mark - Notification Handlers |
| 826 | |
| 827 | // Called when UIApplicationWillResignActiveNotification is received. |
| 828 | - (void)willResignActive:(NSNotification*)notify { |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 829 | if (_webUsageEnabled && self.currentTab) { |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 830 | [SnapshotCacheFactory::GetForBrowserState(_browserState) |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 831 | willBeSavedGreyWhenBackgrounding:self.currentTab.tabId]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 832 | } |
| 833 | } |
| 834 | |
| 835 | // Called when UIApplicationDidEnterBackgroundNotification is received. |
| 836 | - (void)applicationDidEnterBackground:(NSNotification*)notify { |
| 837 | if (!_browserState) |
| 838 | return; |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 839 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 840 | // Evict all the certificate policies except for the current entries of the |
| 841 | // active sessions. |
sdefresne | c92bada | 2017-02-08 14:43:49 | [diff] [blame] | 842 | CleanCertificatePolicyCache( |
| 843 | &_clearPoliciesTaskTracker, |
| 844 | web::WebThread::GetTaskRunnerForThread(web::WebThread::IO), |
sdefresne | 32c0c38 | 2017-02-17 16:37:26 | [diff] [blame] | 845 | web::BrowserState::GetCertificatePolicyCache(_browserState), |
sdefresne | 365a73a | 2017-03-14 15:07:13 | [diff] [blame] | 846 | _webStateList.get()); |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 847 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 848 | // 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. |
sdefresne | fd2aa65 | 2017-05-23 14:23:10 | [diff] [blame] | 853 | if (_webUsageEnabled && self.currentTab) { |
Sylvain Defresne | 528c17b | 2017-06-08 15:00:21 | [diff] [blame] | 854 | [SnapshotCacheFactory::GetForBrowserState(_browserState) |
sdefresne | 2f7781c | 2017-03-02 19:12:46 | [diff] [blame] | 855 | saveGreyInBackgroundForSessionID:self.currentTab.tabId]; |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 856 | } |
| 857 | } |
| 858 | |
Sylvain Defresne | 9439433 | 2018-01-22 13:11:00 | [diff] [blame^] | 859 | #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 | |
sdefresne | 67dfdd6 | 2016-12-19 12:43:12 | [diff] [blame] | 1008 | @end |