OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/browser/tabs/tab_model.h" | 5 #import "ios/chrome/browser/tabs/tab_model.h" |
6 | 6 |
7 #include <cstdint> | 7 #include <cstdint> |
8 #include <utility> | 8 #include <utility> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #import "base/mac/scoped_nsobject.h" | 13 #import "base/mac/scoped_nsobject.h" |
14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
15 #include "base/metrics/user_metrics.h" | 15 #include "base/metrics/user_metrics.h" |
16 #include "base/metrics/user_metrics_action.h" | 16 #include "base/metrics/user_metrics_action.h" |
17 #include "base/strings/sys_string_conversions.h" | 17 #include "base/strings/sys_string_conversions.h" |
18 #include "base/task/cancelable_task_tracker.h" | 18 #include "base/task/cancelable_task_tracker.h" |
19 #include "components/sessions/core/serialized_navigation_entry.h" | 19 #include "components/sessions/core/serialized_navigation_entry.h" |
20 #include "components/sessions/core/session_id.h" | 20 #include "components/sessions/core/session_id.h" |
21 #include "components/sessions/core/tab_restore_service.h" | 21 #include "components/sessions/core/tab_restore_service.h" |
22 #include "components/sessions/ios/ios_live_tab.h" | 22 #include "components/sessions/ios/ios_live_tab.h" |
23 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" | 23 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
24 #include "ios/chrome/browser/chrome_url_constants.h" | 24 #include "ios/chrome/browser/chrome_url_constants.h" |
25 #import "ios/chrome/browser/chrome_url_util.h" | 25 #import "ios/chrome/browser/chrome_url_util.h" |
26 #import "ios/chrome/browser/metrics/tab_usage_recorder.h" | 26 #import "ios/chrome/browser/metrics/tab_usage_recorder.h" |
| 27 #import "ios/chrome/browser/metrics/tab_usage_recorder_web_state_list_observer.h
" |
27 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" | 28 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" |
28 #import "ios/chrome/browser/sessions/session_service.h" | 29 #import "ios/chrome/browser/sessions/session_service.h" |
29 #import "ios/chrome/browser/sessions/session_window.h" | 30 #import "ios/chrome/browser/sessions/session_window.h" |
30 #import "ios/chrome/browser/snapshots/snapshot_cache.h" | 31 #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
| 32 #import "ios/chrome/browser/snapshots/snapshot_cache_web_state_list_observer.h" |
31 #include "ios/chrome/browser/tab_parenting_global_observer.h" | 33 #include "ios/chrome/browser/tab_parenting_global_observer.h" |
32 #import "ios/chrome/browser/tabs/legacy_tab_helper.h" | 34 #import "ios/chrome/browser/tabs/legacy_tab_helper.h" |
33 #import "ios/chrome/browser/tabs/tab.h" | 35 #import "ios/chrome/browser/tabs/tab.h" |
34 #import "ios/chrome/browser/tabs/tab_model_list.h" | 36 #import "ios/chrome/browser/tabs/tab_model_list.h" |
35 #import "ios/chrome/browser/tabs/tab_model_observers.h" | 37 #import "ios/chrome/browser/tabs/tab_model_observers.h" |
36 #import "ios/chrome/browser/tabs/tab_model_observers_bridge.h" | 38 #import "ios/chrome/browser/tabs/tab_model_observers_bridge.h" |
37 #import "ios/chrome/browser/tabs/tab_model_order_controller.h" | 39 #import "ios/chrome/browser/tabs/tab_model_selected_tab_observer.h" |
38 #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" | 40 #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" |
39 #import "ios/chrome/browser/tabs/tab_parenting_observer.h" | 41 #import "ios/chrome/browser/tabs/tab_parenting_observer.h" |
40 #import "ios/chrome/browser/xcallback_parameters.h" | 42 #import "ios/chrome/browser/xcallback_parameters.h" |
41 #import "ios/shared/chrome/browser/tabs/web_state_list.h" | 43 #import "ios/shared/chrome/browser/tabs/web_state_list.h" |
42 #import "ios/shared/chrome/browser/tabs/web_state_list_fast_enumeration_helper.h
" | 44 #import "ios/shared/chrome/browser/tabs/web_state_list_fast_enumeration_helper.h
" |
43 #import "ios/shared/chrome/browser/tabs/web_state_list_metrics_observer.h" | 45 #import "ios/shared/chrome/browser/tabs/web_state_list_metrics_observer.h" |
44 #import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" | 46 #import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" |
45 #import "ios/web/navigation/crw_session_certificate_policy_manager.h" | 47 #import "ios/web/navigation/crw_session_certificate_policy_manager.h" |
46 #import "ios/web/navigation/crw_session_controller.h" | 48 #import "ios/web/navigation/crw_session_controller.h" |
47 #include "ios/web/public/browser_state.h" | 49 #include "ios/web/public/browser_state.h" |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 | 159 |
158 // Used to keep the Tabs alive while the corresponding WebStates are stored | 160 // Used to keep the Tabs alive while the corresponding WebStates are stored |
159 // in the WebStateList (as Tabs currently own their WebState). Remove once | 161 // in the WebStateList (as Tabs currently own their WebState). Remove once |
160 // WebState owns the associated Tab. | 162 // WebState owns the associated Tab. |
161 base::scoped_nsobject<NSMutableSet<Tab*>> _tabRetainer; | 163 base::scoped_nsobject<NSMutableSet<Tab*>> _tabRetainer; |
162 | 164 |
163 // WebStateListObserver bridges to react to modifications of the model (may | 165 // WebStateListObserver bridges to react to modifications of the model (may |
164 // send notification, translate and forward events, update metrics, ...). | 166 // send notification, translate and forward events, update metrics, ...). |
165 std::vector<std::unique_ptr<WebStateListObserver>> _observerBridges; | 167 std::vector<std::unique_ptr<WebStateListObserver>> _observerBridges; |
166 | 168 |
167 // Maintains policy for where new tabs go and the selection when a tab | |
168 // is removed. | |
169 base::scoped_nsobject<TabModelOrderController> _orderController; | |
170 // The delegate for sync. | 169 // The delegate for sync. |
171 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate; | 170 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate; |
172 // Currently selected tab. May be nil. | |
173 base::WeakNSObject<Tab> _currentTab; | |
174 | 171 |
175 // Counters for metrics. | 172 // Counters for metrics. |
176 int _openedTabCount; | 173 WebStateListMetricsObserver* _webStateListMetricsObserver; |
177 int _closedTabCount; | |
178 int _newTabCount; | |
179 | 174 |
180 // Backs up property with the same name. | 175 // Backs up property with the same name. |
181 std::unique_ptr<TabUsageRecorder> _tabUsageRecorder; | 176 std::unique_ptr<TabUsageRecorder> _tabUsageRecorder; |
182 // Backs up property with the same name. | 177 // Backs up property with the same name. |
183 const SessionID _sessionID; | 178 const SessionID _sessionID; |
184 // Saves session's state. | 179 // Saves session's state. |
185 base::scoped_nsobject<SessionServiceIOS> _sessionService; | 180 base::scoped_nsobject<SessionServiceIOS> _sessionService; |
186 // List of TabModelObservers. | 181 // List of TabModelObservers. |
187 base::scoped_nsobject<TabModelObservers> _observers; | 182 base::scoped_nsobject<TabModelObservers> _observers; |
188 | 183 |
(...skipping 16 matching lines...) Expand all Loading... |
205 // TabModelConstants::kTabPositionAutomatically if the caller doesn't have a | 200 // TabModelConstants::kTabPositionAutomatically if the caller doesn't have a |
206 // preference for the position of the tab. | 201 // preference for the position of the tab. |
207 - (Tab*)insertTabWithLoadParams: | 202 - (Tab*)insertTabWithLoadParams: |
208 (const web::NavigationManager::WebLoadParams&)params | 203 (const web::NavigationManager::WebLoadParams&)params |
209 windowName:(NSString*)windowName | 204 windowName:(NSString*)windowName |
210 opener:(Tab*)parentTab | 205 opener:(Tab*)parentTab |
211 openedByDOM:(BOOL)openedByDOM | 206 openedByDOM:(BOOL)openedByDOM |
212 atIndex:(NSUInteger)index | 207 atIndex:(NSUInteger)index |
213 inBackground:(BOOL)inBackground; | 208 inBackground:(BOOL)inBackground; |
214 | 209 |
215 // Call to switch the selected tab. Broadcasts about the change in selection. | |
216 // It's ok for |newTab| to be nil in case the last tab is going away. In that | |
217 // case, the "tab deselected" notification gets sent, but no corresponding | |
218 // "tab selected" notification is sent. |persist| indicates whether or not | |
219 // the tab's state should be persisted in history upon switching. | |
220 - (void)changeSelectedTabFrom:(Tab*)oldTab | |
221 to:(Tab*)newTab | |
222 persistState:(BOOL)persist; | |
223 | |
224 // Tells the snapshot cache the adjacent tab session ids. | |
225 - (void)updateSnapshotCache:(Tab*)tab; | |
226 | |
227 // Helper method that posts a notification with the given name with |tab| | 210 // Helper method that posts a notification with the given name with |tab| |
228 // in the userInfo dictionary under the kTabModelTabKey. | 211 // in the userInfo dictionary under the kTabModelTabKey. |
229 - (void)postNotificationName:(NSString*)notificationName withTab:(Tab*)tab; | 212 - (void)postNotificationName:(NSString*)notificationName withTab:(Tab*)tab; |
230 | 213 |
231 // Helper method to restore a saved session and control if the state should | 214 // Helper method to restore a saved session and control if the state should |
232 // be persisted or not. Used to implement the public -restoreSessionWindow: | 215 // be persisted or not. Used to implement the public -restoreSessionWindow: |
233 // method and restoring session in the initialiser. | 216 // method and restoring session in the initialiser. |
234 - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window | 217 - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window |
235 persistState:(BOOL)persistState; | 218 persistState:(BOOL)persistState; |
236 | 219 |
(...skipping 22 matching lines...) Expand all Loading... |
259 | 242 |
260 #pragma mark - Overriden | 243 #pragma mark - Overriden |
261 | 244 |
262 - (void)dealloc { | 245 - (void)dealloc { |
263 DCHECK([_observers empty]); | 246 DCHECK([_observers empty]); |
264 // browserStateDestroyed should always have been called before destruction. | 247 // browserStateDestroyed should always have been called before destruction. |
265 DCHECK(!_browserState); | 248 DCHECK(!_browserState); |
266 | 249 |
267 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 250 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
268 | 251 |
| 252 // Clear weak pointer to WebStateListMetricsObserver before destroying it. |
| 253 _webStateListMetricsObserver = nullptr; |
| 254 |
269 // Unregister all listeners before closing all the tabs. | 255 // Unregister all listeners before closing all the tabs. |
270 for (const auto& observerBridge : _observerBridges) | 256 for (const auto& observerBridge : _observerBridges) |
271 _webStateList.RemoveObserver(observerBridge.get()); | 257 _webStateList.RemoveObserver(observerBridge.get()); |
272 _observerBridges.clear(); | 258 _observerBridges.clear(); |
273 | 259 |
274 // Make sure the tabs do clean after themselves. It is important for | 260 // Make sure the tabs do clean after themselves. It is important for |
275 // removeObserver: to be called first otherwise a lot of unecessary work will | 261 // removeObserver: to be called first otherwise a lot of unecessary work will |
276 // happen on -closeAllTabs. | 262 // happen on -closeAllTabs. |
277 [self closeAllTabs]; | 263 [self closeAllTabs]; |
278 | 264 |
279 _clearPoliciesTaskTracker.TryCancelAll(); | 265 _clearPoliciesTaskTracker.TryCancelAll(); |
280 | 266 |
281 [super dealloc]; | 267 [super dealloc]; |
282 } | 268 } |
283 | 269 |
284 #pragma mark - Public methods | 270 #pragma mark - Public methods |
285 | 271 |
286 - (Tab*)currentTab { | 272 - (Tab*)currentTab { |
287 return _currentTab.get(); | 273 web::WebState* webState = _webStateList.GetActiveWebState(); |
| 274 return webState ? LegacyTabHelper::GetTabForWebState(webState) : nil; |
288 } | 275 } |
289 | 276 |
290 - (void)setCurrentTab:(Tab*)newTab { | 277 - (void)setCurrentTab:(Tab*)newTab { |
291 DCHECK_NE([self indexOfTab:newTab], static_cast<NSUInteger>(NSNotFound)); | 278 int indexOfTab = _webStateList.GetIndexOfWebState(newTab.webState); |
292 if (_currentTab != newTab) { | 279 DCHECK_NE(indexOfTab, WebStateList::kInvalidIndex); |
293 base::RecordAction(base::UserMetricsAction("MobileTabSwitched")); | 280 _webStateList.ActivateWebStateAt(indexOfTab); |
294 [self updateSnapshotCache:newTab]; | |
295 } | |
296 if (_tabUsageRecorder) { | |
297 _tabUsageRecorder->RecordTabSwitched(_currentTab, newTab); | |
298 } | |
299 [self changeSelectedTabFrom:_currentTab to:newTab persistState:YES]; | |
300 } | 281 } |
301 | 282 |
302 - (TabModelSyncedWindowDelegate*)syncedWindowDelegate { | 283 - (TabModelSyncedWindowDelegate*)syncedWindowDelegate { |
303 return _syncedWindowDelegate.get(); | 284 return _syncedWindowDelegate.get(); |
304 } | 285 } |
305 | 286 |
306 - (TabUsageRecorder*)tabUsageRecorder { | 287 - (TabUsageRecorder*)tabUsageRecorder { |
307 return _tabUsageRecorder.get(); | 288 return _tabUsageRecorder.get(); |
308 } | 289 } |
309 | 290 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 // Set up the usage recorder before tabs are created. | 322 // Set up the usage recorder before tabs are created. |
342 _tabUsageRecorder = base::MakeUnique<TabUsageRecorder>(self); | 323 _tabUsageRecorder = base::MakeUnique<TabUsageRecorder>(self); |
343 } | 324 } |
344 _syncedWindowDelegate = | 325 _syncedWindowDelegate = |
345 base::MakeUnique<TabModelSyncedWindowDelegate>(self); | 326 base::MakeUnique<TabModelSyncedWindowDelegate>(self); |
346 | 327 |
347 // There must be a valid session service defined to consume session windows. | 328 // There must be a valid session service defined to consume session windows. |
348 DCHECK(service); | 329 DCHECK(service); |
349 _sessionService.reset([service retain]); | 330 _sessionService.reset([service retain]); |
350 | 331 |
| 332 _observerBridges.push_back( |
| 333 base::MakeUnique<SnapshotCacheWebStateListObserver>( |
| 334 [SnapshotCache sharedInstance])); |
| 335 if (_tabUsageRecorder) { |
| 336 _observerBridges.push_back( |
| 337 base::MakeUnique<TabUsageRecorderWebStateListObserver>( |
| 338 _tabUsageRecorder.get())); |
| 339 } |
351 _observerBridges.push_back(base::MakeUnique<TabParentingObserver>()); | 340 _observerBridges.push_back(base::MakeUnique<TabParentingObserver>()); |
352 _observerBridges.push_back(base::MakeUnique<WebStateListObserverBridge>( | 341 _observerBridges.push_back(base::MakeUnique<WebStateListObserverBridge>( |
| 342 [[TabModelSelectedTabObserver alloc] initWithTabModel:self])); |
| 343 _observerBridges.push_back(base::MakeUnique<WebStateListObserverBridge>( |
353 [[TabModelObserversBridge alloc] initWithTabModel:self | 344 [[TabModelObserversBridge alloc] initWithTabModel:self |
354 tabModelObservers:_observers.get()])); | 345 tabModelObservers:_observers.get()])); |
355 _observerBridges.push_back(base::MakeUnique<WebStateListMetricsObserver>()); | 346 |
| 347 auto webStateListMetricsObserver = |
| 348 base::MakeUnique<WebStateListMetricsObserver>(); |
| 349 _webStateListMetricsObserver = webStateListMetricsObserver.get(); |
| 350 _observerBridges.push_back(std::move(webStateListMetricsObserver)); |
356 | 351 |
357 for (const auto& observerBridge : _observerBridges) | 352 for (const auto& observerBridge : _observerBridges) |
358 _webStateList.AddObserver(observerBridge.get()); | 353 _webStateList.AddObserver(observerBridge.get()); |
359 | 354 |
360 if (window) { | 355 if (window) { |
361 DCHECK([_observers empty]); | 356 DCHECK([_observers empty]); |
362 // Restore the session and reset the session metrics (as the event have | 357 // Restore the session and reset the session metrics (as the event have |
363 // not been generated by the user but by a cold start cycle). | 358 // not been generated by the user but by a cold start cycle). |
364 [self restoreSessionWindow:window persistState:NO]; | 359 [self restoreSessionWindow:window persistState:NO]; |
365 [self resetSessionMetrics]; | 360 [self resetSessionMetrics]; |
366 } | 361 } |
367 | 362 |
368 _orderController.reset( | |
369 [[TabModelOrderController alloc] initWithTabModel:self]); | |
370 | |
371 // Register for resign active notification. | 363 // Register for resign active notification. |
372 [[NSNotificationCenter defaultCenter] | 364 [[NSNotificationCenter defaultCenter] |
373 addObserver:self | 365 addObserver:self |
374 selector:@selector(willResignActive:) | 366 selector:@selector(willResignActive:) |
375 name:UIApplicationWillResignActiveNotification | 367 name:UIApplicationWillResignActiveNotification |
376 object:nil]; | 368 object:nil]; |
377 // Register for background notification. | 369 // Register for background notification. |
378 [[NSNotificationCenter defaultCenter] | 370 [[NSNotificationCenter defaultCenter] |
379 addObserver:self | 371 addObserver:self |
380 selector:@selector(applicationDidEnterBackground:) | 372 selector:@selector(applicationDidEnterBackground:) |
(...skipping 17 matching lines...) Expand all Loading... |
398 return nil; | 390 return nil; |
399 } | 391 } |
400 | 392 |
401 - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window { | 393 - (BOOL)restoreSessionWindow:(SessionWindowIOS*)window { |
402 return [self restoreSessionWindow:window persistState:YES]; | 394 return [self restoreSessionWindow:window persistState:YES]; |
403 } | 395 } |
404 | 396 |
405 - (void)saveSessionImmediately:(BOOL)immediately { | 397 - (void)saveSessionImmediately:(BOOL)immediately { |
406 // Do nothing if there are tabs in the model but no selected tab. This is | 398 // Do nothing if there are tabs in the model but no selected tab. This is |
407 // a transitional state. | 399 // a transitional state. |
408 if ((!_currentTab && _webStateList.count()) || !_browserState) | 400 if ((!self.currentTab && _webStateList.count()) || !_browserState) |
409 return; | 401 return; |
410 [_sessionService saveWindow:self.windowForSavingSession | 402 [_sessionService saveWindow:self.windowForSavingSession |
411 forBrowserState:_browserState | 403 forBrowserState:_browserState |
412 immediately:immediately]; | 404 immediately:immediately]; |
413 } | 405 } |
414 | 406 |
415 - (Tab*)tabAtIndex:(NSUInteger)index { | 407 - (Tab*)tabAtIndex:(NSUInteger)index { |
416 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); | 408 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
417 return LegacyTabHelper::GetTabForWebState( | 409 return LegacyTabHelper::GetTabForWebState( |
418 _webStateList.GetWebStateAt(static_cast<int>(index))); | 410 _webStateList.GetWebStateAt(static_cast<int>(index))); |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
577 const int insertion_index = static_cast<int>(index); | 569 const int insertion_index = static_cast<int>(index); |
578 _webStateList.InsertWebState(insertion_index, tab.webState, | 570 _webStateList.InsertWebState(insertion_index, tab.webState, |
579 parentTab.webState); | 571 parentTab.webState); |
580 } | 572 } |
581 | 573 |
582 // Persist the session due to a new tab being inserted. If this is a | 574 // Persist the session due to a new tab being inserted. If this is a |
583 // background tab (will not become active), saving now will capture the | 575 // background tab (will not become active), saving now will capture the |
584 // state properly. If it does eventually become active, another save will | 576 // state properly. If it does eventually become active, another save will |
585 // be triggered to properly capture the end result. | 577 // be triggered to properly capture the end result. |
586 [self saveSessionImmediately:NO]; | 578 [self saveSessionImmediately:NO]; |
587 | |
588 ++_newTabCount; | |
589 } | 579 } |
590 | 580 |
591 - (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index opener:(Tab*)parentTab { | 581 - (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index opener:(Tab*)parentTab { |
592 DCHECK(tab); | 582 DCHECK(tab); |
593 DCHECK(![_tabRetainer containsObject:tab]); | 583 DCHECK(![_tabRetainer containsObject:tab]); |
594 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); | 584 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
595 | 585 |
596 [self insertTab:tab | 586 [self insertTab:tab |
597 atIndex:index | 587 atIndex:index |
598 opener:parentTab | 588 opener:parentTab |
(...skipping 24 matching lines...) Expand all Loading... |
623 DCHECK_GE(index, 0); | 613 DCHECK_GE(index, 0); |
624 | 614 |
625 base::scoped_nsobject<Tab> tabSaver([oldTab retain]); | 615 base::scoped_nsobject<Tab> tabSaver([oldTab retain]); |
626 [_tabRetainer removeObject:oldTab]; | 616 [_tabRetainer removeObject:oldTab]; |
627 [_tabRetainer addObject:newTab]; | 617 [_tabRetainer addObject:newTab]; |
628 [newTab setParentTabModel:self]; | 618 [newTab setParentTabModel:self]; |
629 | 619 |
630 _webStateList.ReplaceWebStateAt(index, newTab.webState, | 620 _webStateList.ReplaceWebStateAt(index, newTab.webState, |
631 GetOpenerForTab(self, newTab).webState); | 621 GetOpenerForTab(self, newTab).webState); |
632 | 622 |
633 if (self.currentTab == oldTab) | |
634 [self changeSelectedTabFrom:nil to:newTab persistState:NO]; | |
635 | |
636 [oldTab setParentTabModel:nil]; | 623 [oldTab setParentTabModel:nil]; |
637 [oldTab close]; | 624 [oldTab close]; |
638 } | 625 } |
639 | 626 |
640 - (void)closeTabAtIndex:(NSUInteger)index { | 627 - (void)closeTabAtIndex:(NSUInteger)index { |
641 DCHECK(index < self.count); | 628 DCHECK(index < self.count); |
642 [self closeTab:[self tabAtIndex:index]]; | 629 [self closeTab:[self tabAtIndex:index]]; |
643 } | 630 } |
644 | 631 |
645 - (void)closeTab:(Tab*)tab { | 632 - (void)closeTab:(Tab*)tab { |
646 // Ensure the tab stays alive long enough for us to send out the | 633 // Ensure the tab stays alive long enough for us to send out the |
647 // notice of its destruction to the delegate. | 634 // notice of its destruction to the delegate. |
648 [_observers tabModel:self willRemoveTab:tab]; | 635 [_observers tabModel:self willRemoveTab:tab]; |
649 [tab close]; // Note it is not safe to access the tab after 'close'. | 636 [tab close]; // Note it is not safe to access the tab after 'close'. |
650 } | 637 } |
651 | 638 |
652 - (void)closeAllTabs { | 639 - (void)closeAllTabs { |
653 // If this changes, _closedTabCount metrics need to be adjusted. | |
654 for (NSInteger i = self.count - 1; i >= 0; --i) | 640 for (NSInteger i = self.count - 1; i >= 0; --i) |
655 [self closeTabAtIndex:i]; | 641 [self closeTabAtIndex:i]; |
656 [[NSNotificationCenter defaultCenter] | 642 [[NSNotificationCenter defaultCenter] |
657 postNotificationName:kTabModelAllTabsDidCloseNotification | 643 postNotificationName:kTabModelAllTabsDidCloseNotification |
658 object:self]; | 644 object:self]; |
659 } | 645 } |
660 | 646 |
661 - (void)haltAllTabs { | 647 - (void)haltAllTabs { |
662 for (Tab* tab in self) { | 648 for (Tab* tab in self) { |
663 [tab terminateNetworkActivity]; | 649 [tab terminateNetworkActivity]; |
664 } | 650 } |
665 } | 651 } |
666 | 652 |
667 - (void)notifyTabChanged:(Tab*)tab { | 653 - (void)notifyTabChanged:(Tab*)tab { |
668 [_observers tabModel:self didChangeTab:tab]; | 654 [_observers tabModel:self didChangeTab:tab]; |
669 } | 655 } |
670 | 656 |
671 - (void)addObserver:(id<TabModelObserver>)observer { | 657 - (void)addObserver:(id<TabModelObserver>)observer { |
672 [_observers addObserver:observer]; | 658 [_observers addObserver:observer]; |
673 } | 659 } |
674 | 660 |
675 - (void)removeObserver:(id<TabModelObserver>)observer { | 661 - (void)removeObserver:(id<TabModelObserver>)observer { |
676 [_observers removeObserver:observer]; | 662 [_observers removeObserver:observer]; |
677 } | 663 } |
678 | 664 |
679 - (void)resetSessionMetrics { | 665 - (void)resetSessionMetrics { |
680 _closedTabCount = 0; | 666 if (_webStateListMetricsObserver) |
681 _openedTabCount = 0; | 667 _webStateListMetricsObserver->ResetSessionMetrics(); |
682 _newTabCount = 0; | |
683 } | 668 } |
684 | 669 |
685 - (void)recordSessionMetrics { | 670 - (void)recordSessionMetrics { |
686 UMA_HISTOGRAM_CUSTOM_COUNTS("Session.ClosedTabCounts", _closedTabCount, 1, | 671 if (_webStateListMetricsObserver) |
687 200, 50); | 672 _webStateListMetricsObserver->RecordSessionMetrics(); |
688 UMA_HISTOGRAM_CUSTOM_COUNTS("Session.OpenedTabCounts", _openedTabCount, 1, | |
689 200, 50); | |
690 UMA_HISTOGRAM_CUSTOM_COUNTS("Session.NewTabCounts", _newTabCount, 1, 200, 50); | |
691 } | 673 } |
692 | 674 |
693 - (void)notifyTabSnapshotChanged:(Tab*)tab withImage:(UIImage*)image { | 675 - (void)notifyTabSnapshotChanged:(Tab*)tab withImage:(UIImage*)image { |
694 DCHECK([NSThread isMainThread]); | 676 DCHECK([NSThread isMainThread]); |
695 [_observers tabModel:self didChangeTabSnapshot:tab withImage:image]; | 677 [_observers tabModel:self didChangeTabSnapshot:tab withImage:image]; |
696 } | 678 } |
697 | 679 |
698 - (void)resetAllWebViews { | 680 - (void)resetAllWebViews { |
699 for (Tab* tab in self) { | 681 for (Tab* tab in self) { |
700 [tab.webController reinitializeWebViewAndReload:(tab == _currentTab)]; | 682 [tab.webController reinitializeWebViewAndReload:(tab == self.currentTab)]; |
701 } | 683 } |
702 } | 684 } |
703 | 685 |
704 - (void)setWebUsageEnabled:(BOOL)webUsageEnabled { | 686 - (void)setWebUsageEnabled:(BOOL)webUsageEnabled { |
705 if (webUsageEnabled_ == webUsageEnabled) | 687 if (webUsageEnabled_ == webUsageEnabled) |
706 return; | 688 return; |
707 webUsageEnabled_ = webUsageEnabled; | 689 webUsageEnabled_ = webUsageEnabled; |
708 for (Tab* tab in self) { | 690 for (Tab* tab in self) { |
709 tab.webUsageEnabled = webUsageEnabled; | 691 tab.webUsageEnabled = webUsageEnabled; |
710 } | 692 } |
711 } | 693 } |
712 | 694 |
713 - (void)setPrimary:(BOOL)primary { | 695 - (void)setPrimary:(BOOL)primary { |
714 if (_tabUsageRecorder) | 696 if (_tabUsageRecorder) |
715 _tabUsageRecorder->RecordPrimaryTabModelChange(primary, _currentTab); | 697 _tabUsageRecorder->RecordPrimaryTabModelChange(primary, self.currentTab); |
716 } | 698 } |
717 | 699 |
718 - (NSSet*)currentlyReferencedExternalFiles { | 700 - (NSSet*)currentlyReferencedExternalFiles { |
719 NSMutableSet* referencedFiles = [NSMutableSet set]; | 701 NSMutableSet* referencedFiles = [NSMutableSet set]; |
720 if (!_browserState) | 702 if (!_browserState) |
721 return referencedFiles; | 703 return referencedFiles; |
722 // Check the currently open tabs for external files. | 704 // Check the currently open tabs for external files. |
723 for (Tab* tab in self) { | 705 for (Tab* tab in self) { |
724 if (UrlIsExternalFileReference(tab.url)) { | 706 if (UrlIsExternalFileReference(tab.url)) { |
725 NSString* fileName = base::SysUTF8ToNSString(tab.url.ExtractFileName()); | 707 NSString* fileName = base::SysUTF8ToNSString(tab.url.ExtractFileName()); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
769 ? IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState) | 751 ? IOSChromeTabRestoreServiceFactory::GetForBrowserState(_browserState) |
770 : nullptr; | 752 : nullptr; |
771 web::NavigationManagerImpl* navigationManager = [closedTab navigationManager]; | 753 web::NavigationManagerImpl* navigationManager = [closedTab navigationManager]; |
772 DCHECK(navigationManager); | 754 DCHECK(navigationManager); |
773 int itemCount = navigationManager->GetItemCount(); | 755 int itemCount = navigationManager->GetItemCount(); |
774 if (restoreService && (![self isNTPTab:closedTab] || itemCount > 1)) { | 756 if (restoreService && (![self isNTPTab:closedTab] || itemCount > 1)) { |
775 restoreService->CreateHistoricalTab( | 757 restoreService->CreateHistoricalTab( |
776 sessions::IOSLiveTab::GetForWebState(closedTab.webState), | 758 sessions::IOSLiveTab::GetForWebState(closedTab.webState), |
777 closedTabIndex); | 759 closedTabIndex); |
778 } | 760 } |
779 // This needs to be called before the tab is removed from the list. | |
780 Tab* newSelection = | |
781 [_orderController determineNewSelectedTabFromRemovedTab:closedTab]; | |
782 | 761 |
783 base::scoped_nsobject<Tab> kungFuDeathGrip([closedTab retain]); | 762 base::scoped_nsobject<Tab> kungFuDeathGrip([closedTab retain]); |
784 | 763 |
785 // If closing the current tab, clear |_currentTab| before sending any | 764 // If a non-current Tab is closed, save the session (it will be saved by |
786 // notification. This avoids various parts of the code getting confused | 765 // TabModelObserversBridge if the currentTab has been closed). |
787 // when the current tab isn't in the tab model. | 766 BOOL needToSaveSession = (closedTab != self.currentTab); |
788 Tab* savedCurrentTab = _currentTab; | |
789 if (closedTab == _currentTab) | |
790 _currentTab.reset(nil); | |
791 | 767 |
792 DCHECK([_tabRetainer containsObject:closedTab]); | 768 DCHECK([_tabRetainer containsObject:closedTab]); |
793 [_tabRetainer removeObject:closedTab]; | 769 [_tabRetainer removeObject:closedTab]; |
794 | 770 |
795 _webStateList.DetachWebStateAt(closedTabIndex); | 771 _webStateList.DetachWebStateAt(closedTabIndex); |
796 | 772 |
797 // Current tab has closed, update the selected tab and swap in its | 773 if (needToSaveSession) |
798 // contents. There is nothing to do if a non-selected tab is closed as | |
799 // the selection isn't index-based, therefore it hasn't changed. | |
800 // -changeSelectedTabFrom: will persist the state change, so only do it | |
801 // if the selection isn't changing. | |
802 if (closedTab == savedCurrentTab) { | |
803 [self changeSelectedTabFrom:closedTab to:newSelection persistState:NO]; | |
804 } else { | |
805 [self saveSessionImmediately:NO]; | 774 [self saveSessionImmediately:NO]; |
806 } | |
807 ++_closedTabCount; | |
808 } | 775 } |
809 | 776 |
810 - (void)navigationCommittedInTab:(Tab*)tab { | 777 - (void)navigationCommittedInTab:(Tab*)tab { |
811 if (self.offTheRecord) | 778 if (self.offTheRecord) |
812 return; | 779 return; |
813 if (![tab navigationManager]) | 780 if (![tab navigationManager]) |
814 return; | 781 return; |
815 | 782 |
816 // See if the navigation was within a page; if so ignore it. | 783 // See if the navigation was within a page; if so ignore it. |
817 web::NavigationItem* previousItem = | 784 web::NavigationItem* previousItem = |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
851 count++; | 818 count++; |
852 } | 819 } |
853 return count; | 820 return count; |
854 } | 821 } |
855 | 822 |
856 #pragma mark - Private methods | 823 #pragma mark - Private methods |
857 | 824 |
858 - (SessionWindowIOS*)windowForSavingSession { | 825 - (SessionWindowIOS*)windowForSavingSession { |
859 // Background tabs will already have their state preserved, but not the | 826 // Background tabs will already have their state preserved, but not the |
860 // fg tab. Do it now. | 827 // fg tab. Do it now. |
861 [_currentTab recordStateInHistory]; | 828 [self.currentTab recordStateInHistory]; |
862 | 829 |
863 // Build the array of sessions. Copy the session objects as the saving will | 830 // Build the array of sessions. Copy the session objects as the saving will |
864 // be done on a separate thread. | 831 // be done on a separate thread. |
865 // TODO(crbug.com/661986): This could get expensive especially since this | 832 // TODO(crbug.com/661986): This could get expensive especially since this |
866 // window may never be saved (if another call comes in before the delay). | 833 // window may never be saved (if another call comes in before the delay). |
867 SessionWindowIOS* window = [[[SessionWindowIOS alloc] init] autorelease]; | 834 SessionWindowIOS* window = [[[SessionWindowIOS alloc] init] autorelease]; |
868 for (Tab* tab in self) { | 835 for (Tab* tab in self) { |
869 web::WebState* webState = tab.webState; | 836 web::WebState* webState = tab.webState; |
870 DCHECK(webState); | 837 DCHECK(webState); |
871 [window addSerializedSessionStorage:webState->BuildSessionStorage()]; | 838 [window addSerializedSessionStorage:webState->BuildSessionStorage()]; |
872 } | 839 } |
873 window.selectedIndex = [self indexOfTab:_currentTab]; | 840 window.selectedIndex = [self indexOfTab:self.currentTab]; |
874 return window; | 841 return window; |
875 } | 842 } |
876 | 843 |
877 - (BOOL)isNTPTab:(Tab*)tab { | 844 - (BOOL)isNTPTab:(Tab*)tab { |
878 std::string host = tab.url.host(); | 845 std::string host = tab.url.host(); |
879 return host == kChromeUINewTabHost || host == kChromeUIBookmarksHost; | 846 return host == kChromeUINewTabHost || host == kChromeUIBookmarksHost; |
880 } | 847 } |
881 | 848 |
882 - (Tab*)insertTabWithLoadParams: | 849 - (Tab*)insertTabWithLoadParams: |
883 (const web::NavigationManager::WebLoadParams&)params | 850 (const web::NavigationManager::WebLoadParams&)params |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
915 postNotificationName:kTabModelNewTabWillOpenNotification | 882 postNotificationName:kTabModelNewTabWillOpenNotification |
916 object:self | 883 object:self |
917 userInfo:userInfo]; | 884 userInfo:userInfo]; |
918 | 885 |
919 if (!inBackground) | 886 if (!inBackground) |
920 [self setCurrentTab:tab]; | 887 [self setCurrentTab:tab]; |
921 | 888 |
922 return tab; | 889 return tab; |
923 } | 890 } |
924 | 891 |
925 - (void)changeSelectedTabFrom:(Tab*)oldTab | |
926 to:(Tab*)newTab | |
927 persistState:(BOOL)persist { | |
928 if (oldTab) { | |
929 // Save state, such as scroll position, before switching tabs. | |
930 if (oldTab != newTab && persist) | |
931 [oldTab recordStateInHistory]; | |
932 [self postNotificationName:kTabModelTabDeselectedNotification | |
933 withTab:oldTab]; | |
934 } | |
935 | |
936 // No Tab to select (e.g. the last Tab has been closed). | |
937 if ([self indexOfTab:newTab] == NSNotFound) | |
938 return; | |
939 | |
940 _currentTab.reset(newTab); | |
941 if (newTab) { | |
942 [_observers tabModel:self | |
943 didChangeActiveTab:newTab | |
944 previousTab:oldTab | |
945 atIndex:[self indexOfTab:newTab]]; | |
946 [newTab updateLastVisitedTimestamp]; | |
947 ++_openedTabCount; | |
948 } | |
949 BOOL loadingFinished = [newTab.webController loadPhase] == web::PAGE_LOADED; | |
950 if (loadingFinished) { | |
951 // Persist the session state. | |
952 [self saveSessionImmediately:NO]; | |
953 } | |
954 } | |
955 | |
956 - (void)updateSnapshotCache:(Tab*)tab { | |
957 NSMutableSet* set = [NSMutableSet set]; | |
958 NSUInteger index = [self indexOfTab:tab]; | |
959 if (index > 0) { | |
960 Tab* previousTab = [self tabAtIndex:(index - 1)]; | |
961 [set addObject:previousTab.tabId]; | |
962 } | |
963 if (index < self.count - 1) { | |
964 Tab* nextTab = [self tabAtIndex:(index + 1)]; | |
965 [set addObject:nextTab.tabId]; | |
966 } | |
967 [SnapshotCache sharedInstance].pinnedIDs = set; | |
968 } | |
969 | |
970 - (void)postNotificationName:(NSString*)notificationName withTab:(Tab*)tab { | 892 - (void)postNotificationName:(NSString*)notificationName withTab:(Tab*)tab { |
971 // A scoped_nsobject is used rather than an NSDictionary with static | 893 // A scoped_nsobject is used rather than an NSDictionary with static |
972 // initializer dictionaryWithObject, because that approach adds the dictionary | 894 // initializer dictionaryWithObject, because that approach adds the dictionary |
973 // to the autorelease pool, which in turn holds Tab alive longer than | 895 // to the autorelease pool, which in turn holds Tab alive longer than |
974 // necessary. | 896 // necessary. |
975 base::scoped_nsobject<NSDictionary> userInfo( | 897 base::scoped_nsobject<NSDictionary> userInfo( |
976 [[NSDictionary alloc] initWithObjectsAndKeys:tab, kTabModelTabKey, nil]); | 898 [[NSDictionary alloc] initWithObjectsAndKeys:tab, kTabModelTabKey, nil]); |
977 [[NSNotificationCenter defaultCenter] postNotificationName:notificationName | 899 [[NSNotificationCenter defaultCenter] postNotificationName:notificationName |
978 object:self | 900 object:self |
979 userInfo:userInfo]; | 901 userInfo:userInfo]; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1028 DCHECK(opener.webState); | 950 DCHECK(opener.webState); |
1029 _webStateList.SetOpenerOfWebStateAt(index, opener.webState); | 951 _webStateList.SetOpenerOfWebStateAt(index, opener.webState); |
1030 } | 952 } |
1031 } | 953 } |
1032 | 954 |
1033 // Update the selected tab if there was a selected Tab in the saved session. | 955 // Update the selected tab if there was a selected Tab in the saved session. |
1034 if (window.selectedIndex != NSNotFound) { | 956 if (window.selectedIndex != NSNotFound) { |
1035 NSUInteger selectedIndex = window.selectedIndex + oldCount; | 957 NSUInteger selectedIndex = window.selectedIndex + oldCount; |
1036 DCHECK_LT(selectedIndex, self.count); | 958 DCHECK_LT(selectedIndex, self.count); |
1037 DCHECK([self tabAtIndex:selectedIndex]); | 959 DCHECK([self tabAtIndex:selectedIndex]); |
1038 [self changeSelectedTabFrom:_currentTab | 960 |
1039 to:[self tabAtIndex:selectedIndex] | 961 if (persistState && self.currentTab) |
1040 persistState:persistState]; | 962 [self.currentTab recordStateInHistory]; |
| 963 _webStateList.ActivateWebStateAt(static_cast<int>(selectedIndex)); |
1041 } | 964 } |
1042 | 965 |
1043 // If there was only one tab and it was the new tab page, clobber it. | 966 // If there was only one tab and it was the new tab page, clobber it. |
1044 BOOL closedNTPTab = NO; | 967 BOOL closedNTPTab = NO; |
1045 if (oldCount == 1) { | 968 if (oldCount == 1) { |
1046 Tab* tab = [self tabAtIndex:0]; | 969 Tab* tab = [self tabAtIndex:0]; |
1047 if (tab.url == GURL(kChromeUINewTabURL)) { | 970 if (tab.url == GURL(kChromeUINewTabURL)) { |
1048 [self closeTab:tab]; | 971 [self closeTab:tab]; |
1049 closedNTPTab = YES; | 972 closedNTPTab = YES; |
1050 oldCount = 0; | 973 oldCount = 0; |
1051 } | 974 } |
1052 } | 975 } |
1053 if (_tabUsageRecorder) { | 976 if (_tabUsageRecorder) { |
1054 NSMutableArray<Tab*>* restoredTabs = | 977 NSMutableArray<Tab*>* restoredTabs = |
1055 [NSMutableArray arrayWithCapacity:_webStateList.count() - oldCount]; | 978 [NSMutableArray arrayWithCapacity:_webStateList.count() - oldCount]; |
1056 for (int index = oldCount; index < _webStateList.count(); ++index) { | 979 for (int index = oldCount; index < _webStateList.count(); ++index) { |
1057 web::WebState* webState = _webStateList.GetWebStateAt(index); | 980 web::WebState* webState = _webStateList.GetWebStateAt(index); |
1058 [restoredTabs addObject:LegacyTabHelper::GetTabForWebState(webState)]; | 981 [restoredTabs addObject:LegacyTabHelper::GetTabForWebState(webState)]; |
1059 } | 982 } |
1060 _tabUsageRecorder->InitialRestoredTabs(_currentTab, restoredTabs); | 983 _tabUsageRecorder->InitialRestoredTabs(self.currentTab, restoredTabs); |
1061 } | 984 } |
1062 return closedNTPTab; | 985 return closedNTPTab; |
1063 } | 986 } |
1064 | 987 |
1065 #pragma mark - Notification Handlers | 988 #pragma mark - Notification Handlers |
1066 | 989 |
1067 // Called when UIApplicationWillResignActiveNotification is received. | 990 // Called when UIApplicationWillResignActiveNotification is received. |
1068 - (void)willResignActive:(NSNotification*)notify { | 991 - (void)willResignActive:(NSNotification*)notify { |
1069 if (webUsageEnabled_ && _currentTab) { | 992 if (webUsageEnabled_ && self.currentTab) { |
1070 [[SnapshotCache sharedInstance] | 993 [[SnapshotCache sharedInstance] |
1071 willBeSavedGreyWhenBackgrounding:_currentTab.get().tabId]; | 994 willBeSavedGreyWhenBackgrounding:self.currentTab.tabId]; |
1072 } | 995 } |
1073 } | 996 } |
1074 | 997 |
1075 // Called when UIApplicationDidEnterBackgroundNotification is received. | 998 // Called when UIApplicationDidEnterBackgroundNotification is received. |
1076 - (void)applicationDidEnterBackground:(NSNotification*)notify { | 999 - (void)applicationDidEnterBackground:(NSNotification*)notify { |
1077 if (!_browserState) | 1000 if (!_browserState) |
1078 return; | 1001 return; |
1079 | 1002 |
1080 // Evict all the certificate policies except for the current entries of the | 1003 // Evict all the certificate policies except for the current entries of the |
1081 // active sessions. | 1004 // active sessions. |
1082 CleanCertificatePolicyCache( | 1005 CleanCertificatePolicyCache( |
1083 &_clearPoliciesTaskTracker, | 1006 &_clearPoliciesTaskTracker, |
1084 web::WebThread::GetTaskRunnerForThread(web::WebThread::IO), | 1007 web::WebThread::GetTaskRunnerForThread(web::WebThread::IO), |
1085 web::BrowserState::GetCertificatePolicyCache(_browserState), | 1008 web::BrowserState::GetCertificatePolicyCache(_browserState), |
1086 &_webStateList); | 1009 &_webStateList); |
1087 | 1010 |
1088 if (_tabUsageRecorder) | 1011 if (_tabUsageRecorder) |
1089 _tabUsageRecorder->AppDidEnterBackground(); | 1012 _tabUsageRecorder->AppDidEnterBackground(); |
1090 | 1013 |
1091 // Normally, the session is saved after some timer expires but since the app | 1014 // Normally, the session is saved after some timer expires but since the app |
1092 // is about to enter the background send YES to save the session immediately. | 1015 // is about to enter the background send YES to save the session immediately. |
1093 [self saveSessionImmediately:YES]; | 1016 [self saveSessionImmediately:YES]; |
1094 | 1017 |
1095 // Write out a grey version of the current website to disk. | 1018 // Write out a grey version of the current website to disk. |
1096 if (webUsageEnabled_ && _currentTab) { | 1019 if (webUsageEnabled_ && self.currentTab) { |
1097 [[SnapshotCache sharedInstance] | 1020 [[SnapshotCache sharedInstance] |
1098 saveGreyInBackgroundForSessionID:_currentTab.get().tabId]; | 1021 saveGreyInBackgroundForSessionID:self.currentTab.tabId]; |
1099 } | 1022 } |
1100 } | 1023 } |
1101 | 1024 |
1102 // Called when UIApplicationWillEnterForegroundNotification is received. | 1025 // Called when UIApplicationWillEnterForegroundNotification is received. |
1103 - (void)applicationWillEnterForeground:(NSNotification*)notify { | 1026 - (void)applicationWillEnterForeground:(NSNotification*)notify { |
1104 if (_tabUsageRecorder) { | 1027 if (_tabUsageRecorder) { |
1105 _tabUsageRecorder->AppWillEnterForeground(); | 1028 _tabUsageRecorder->AppWillEnterForeground(); |
1106 } | 1029 } |
1107 } | 1030 } |
1108 | 1031 |
(...skipping 26 matching lines...) Expand all Loading... |
1135 web::NavigationManager::WebLoadParams params(URL); | 1058 web::NavigationManager::WebLoadParams params(URL); |
1136 params.referrer = referrer; | 1059 params.referrer = referrer; |
1137 params.transition_type = ui::PAGE_TRANSITION_TYPED; | 1060 params.transition_type = ui::PAGE_TRANSITION_TYPED; |
1138 [[tab webController] loadWithParams:params]; | 1061 [[tab webController] loadWithParams:params]; |
1139 [tab webController].webUsageEnabled = webUsageEnabled_; | 1062 [tab webController].webUsageEnabled = webUsageEnabled_; |
1140 [self insertTab:tab atIndex:index opener:parentTab]; | 1063 [self insertTab:tab atIndex:index opener:parentTab]; |
1141 return tab; | 1064 return tab; |
1142 } | 1065 } |
1143 | 1066 |
1144 @end | 1067 @end |
OLD | NEW |