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 |
(...skipping 15 matching lines...) Expand all Loading... |
26 #import "ios/chrome/browser/metrics/tab_usage_recorder.h" | 26 #import "ios/chrome/browser/metrics/tab_usage_recorder.h" |
27 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" | 27 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" |
28 #import "ios/chrome/browser/sessions/session_service.h" | 28 #import "ios/chrome/browser/sessions/session_service.h" |
29 #import "ios/chrome/browser/sessions/session_window.h" | 29 #import "ios/chrome/browser/sessions/session_window.h" |
30 #import "ios/chrome/browser/snapshots/snapshot_cache.h" | 30 #import "ios/chrome/browser/snapshots/snapshot_cache.h" |
31 #include "ios/chrome/browser/tab_parenting_global_observer.h" | 31 #include "ios/chrome/browser/tab_parenting_global_observer.h" |
32 #import "ios/chrome/browser/tabs/legacy_tab_helper.h" | 32 #import "ios/chrome/browser/tabs/legacy_tab_helper.h" |
33 #import "ios/chrome/browser/tabs/tab.h" | 33 #import "ios/chrome/browser/tabs/tab.h" |
34 #import "ios/chrome/browser/tabs/tab_model_list.h" | 34 #import "ios/chrome/browser/tabs/tab_model_list.h" |
35 #import "ios/chrome/browser/tabs/tab_model_observers.h" | 35 #import "ios/chrome/browser/tabs/tab_model_observers.h" |
| 36 #import "ios/chrome/browser/tabs/tab_model_observers_bridge.h" |
36 #import "ios/chrome/browser/tabs/tab_model_order_controller.h" | 37 #import "ios/chrome/browser/tabs/tab_model_order_controller.h" |
37 #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" | 38 #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h" |
| 39 #import "ios/chrome/browser/tabs/tab_parenting_observer.h" |
38 #import "ios/chrome/browser/xcallback_parameters.h" | 40 #import "ios/chrome/browser/xcallback_parameters.h" |
39 #import "ios/shared/chrome/browser/tabs/web_state_list.h" | 41 #import "ios/shared/chrome/browser/tabs/web_state_list.h" |
40 #import "ios/shared/chrome/browser/tabs/web_state_list_fast_enumeration_helper.h
" | 42 #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" |
41 #import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" | 44 #import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" |
42 #import "ios/web/navigation/crw_session_certificate_policy_manager.h" | 45 #import "ios/web/navigation/crw_session_certificate_policy_manager.h" |
43 #import "ios/web/navigation/crw_session_controller.h" | 46 #import "ios/web/navigation/crw_session_controller.h" |
44 #include "ios/web/public/browser_state.h" | 47 #include "ios/web/public/browser_state.h" |
45 #include "ios/web/public/certificate_policy_cache.h" | 48 #include "ios/web/public/certificate_policy_cache.h" |
46 #include "ios/web/public/navigation_item.h" | 49 #include "ios/web/public/navigation_item.h" |
47 #import "ios/web/public/navigation_manager.h" | 50 #import "ios/web/public/navigation_manager.h" |
48 #include "ios/web/public/web_thread.h" | 51 #include "ios/web/public/web_thread.h" |
49 #import "ios/web/web_state/ui/crw_web_controller.h" | 52 #import "ios/web/web_state/ui/crw_web_controller.h" |
50 #import "ios/web/web_state/web_state_impl.h" | 53 #import "ios/web/web_state/web_state_impl.h" |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 | 136 |
134 // Helper providing NSFastEnumeration implementation over the WebStateList. | 137 // Helper providing NSFastEnumeration implementation over the WebStateList. |
135 base::scoped_nsobject<WebStateListFastEnumerationHelper> | 138 base::scoped_nsobject<WebStateListFastEnumerationHelper> |
136 _fastEnumerationHelper; | 139 _fastEnumerationHelper; |
137 | 140 |
138 // Used to keep the Tabs alive while the corresponding WebStates are stored | 141 // Used to keep the Tabs alive while the corresponding WebStates are stored |
139 // in the WebStateList (as Tabs currently own their WebState). Remove once | 142 // in the WebStateList (as Tabs currently own their WebState). Remove once |
140 // WebState owns the associated Tab. | 143 // WebState owns the associated Tab. |
141 base::scoped_nsobject<NSMutableSet<Tab*>> _tabRetainer; | 144 base::scoped_nsobject<NSMutableSet<Tab*>> _tabRetainer; |
142 | 145 |
| 146 // WebStateListObserver bridges to react to modifications of the model (may |
| 147 // send notification, translate and forward events, update metrics, ...). |
| 148 std::vector<std::unique_ptr<WebStateListObserver>> _observerBridges; |
| 149 |
143 // Maintains policy for where new tabs go and the selection when a tab | 150 // Maintains policy for where new tabs go and the selection when a tab |
144 // is removed. | 151 // is removed. |
145 base::scoped_nsobject<TabModelOrderController> _orderController; | 152 base::scoped_nsobject<TabModelOrderController> _orderController; |
146 // The delegate for sync. | 153 // The delegate for sync. |
147 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate; | 154 std::unique_ptr<TabModelSyncedWindowDelegate> _syncedWindowDelegate; |
148 // Currently selected tab. May be nil. | 155 // Currently selected tab. May be nil. |
149 base::WeakNSObject<Tab> _currentTab; | 156 base::WeakNSObject<Tab> _currentTab; |
150 | 157 |
151 // Counters for metrics. | 158 // Counters for metrics. |
152 int _openedTabCount; | 159 int _openedTabCount; |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 @synthesize webUsageEnabled = webUsageEnabled_; | 226 @synthesize webUsageEnabled = webUsageEnabled_; |
220 | 227 |
221 #pragma mark - Overriden | 228 #pragma mark - Overriden |
222 | 229 |
223 - (void)dealloc { | 230 - (void)dealloc { |
224 DCHECK([_observers empty]); | 231 DCHECK([_observers empty]); |
225 // browserStateDestroyed should always have been called before destruction. | 232 // browserStateDestroyed should always have been called before destruction. |
226 DCHECK(!_browserState); | 233 DCHECK(!_browserState); |
227 | 234 |
228 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 235 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 236 |
| 237 // Unregister all listeners before closing all the tabs. |
| 238 for (const auto& observerBridge : _observerBridges) |
| 239 _webStateList.RemoveObserver(observerBridge.get()); |
| 240 _observerBridges.clear(); |
| 241 |
229 // Make sure the tabs do clean after themselves. It is important for | 242 // Make sure the tabs do clean after themselves. It is important for |
230 // removeObserver: to be called first otherwise a lot of unecessary work will | 243 // removeObserver: to be called first otherwise a lot of unecessary work will |
231 // happen on -closeAllTabs. | 244 // happen on -closeAllTabs. |
232 [self closeAllTabs]; | 245 [self closeAllTabs]; |
233 | 246 |
234 _clearPoliciesTaskTracker.TryCancelAll(); | 247 _clearPoliciesTaskTracker.TryCancelAll(); |
235 | 248 |
236 [super dealloc]; | 249 [super dealloc]; |
237 } | 250 } |
238 | 251 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 sessionService:(SessionServiceIOS*)service | 292 sessionService:(SessionServiceIOS*)service |
280 browserState:(ios::ChromeBrowserState*)browserState { | 293 browserState:(ios::ChromeBrowserState*)browserState { |
281 if ((self = [super init])) { | 294 if ((self = [super init])) { |
282 _tabRetainer.reset([[NSMutableSet alloc] init]); | 295 _tabRetainer.reset([[NSMutableSet alloc] init]); |
283 _observers.reset([[TabModelObservers observers] retain]); | 296 _observers.reset([[TabModelObservers observers] retain]); |
284 | 297 |
285 _fastEnumerationHelper.reset([[WebStateListFastEnumerationHelper alloc] | 298 _fastEnumerationHelper.reset([[WebStateListFastEnumerationHelper alloc] |
286 initWithWebStateList:&_webStateList | 299 initWithWebStateList:&_webStateList |
287 proxyFactory:[[TabModelWebStateProxyFactory alloc] init]]); | 300 proxyFactory:[[TabModelWebStateProxyFactory alloc] init]]); |
288 | 301 |
| 302 _observerBridges.push_back(base::MakeUnique<TabParentingObserver>()); |
| 303 _observerBridges.push_back(base::MakeUnique<WebStateListObserverBridge>( |
| 304 [[TabModelObserversBridge alloc] initWithTabModel:self |
| 305 tabModelObservers:_observers.get()])); |
| 306 _observerBridges.push_back(base::MakeUnique<WebStateListMetricsObserver>()); |
| 307 for (const auto& observerBridge : _observerBridges) |
| 308 _webStateList.AddObserver(observerBridge.get()); |
| 309 |
289 _browserState = browserState; | 310 _browserState = browserState; |
290 DCHECK(_browserState); | 311 DCHECK(_browserState); |
291 | 312 |
292 // There must be a valid session service defined to consume session windows. | 313 // There must be a valid session service defined to consume session windows. |
293 DCHECK(service); | 314 DCHECK(service); |
294 _sessionService.reset([service retain]); | 315 _sessionService.reset([service retain]); |
295 | 316 |
296 // Normal browser states are the only ones to get tab restore. Tab sync | 317 // Normal browser states are the only ones to get tab restore. Tab sync |
297 // handles incognito browser states by filtering on profile, so it's | 318 // handles incognito browser states by filtering on profile, so it's |
298 // important to the backend code to always have a sync window delegate. | 319 // important to the backend code to always have a sync window delegate. |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
529 [[Tab alloc] initWithWebState:std::move(webState) model:self]); | 550 [[Tab alloc] initWithWebState:std::move(webState) model:self]); |
530 [tab webController].webUsageEnabled = webUsageEnabled_; | 551 [tab webController].webUsageEnabled = webUsageEnabled_; |
531 [self insertTab:tab atIndex:index]; | 552 [self insertTab:tab atIndex:index]; |
532 return tab; | 553 return tab; |
533 } | 554 } |
534 | 555 |
535 - (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index { | 556 - (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index { |
536 DCHECK(tab); | 557 DCHECK(tab); |
537 DCHECK(![_tabRetainer containsObject:tab]); | 558 DCHECK(![_tabRetainer containsObject:tab]); |
538 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); | 559 DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
539 [tab fetchFavicon]; | |
540 | 560 |
541 [_tabRetainer addObject:tab]; | 561 [_tabRetainer addObject:tab]; |
542 _webStateList.InsertWebState(static_cast<int>(index), tab.webState); | 562 _webStateList.InsertWebState(static_cast<int>(index), tab.webState); |
543 TabParentingGlobalObserver::GetInstance()->OnTabParented(tab.webState); | |
544 [_observers tabModel:self didInsertTab:tab atIndex:index inForeground:NO]; | |
545 [_observers tabModelDidChangeTabCount:self]; | |
546 | 563 |
547 base::RecordAction(base::UserMetricsAction("MobileNewTabOpened")); | |
548 // Persist the session due to a new tab being inserted. If this is a | 564 // Persist the session due to a new tab being inserted. If this is a |
549 // background tab (will not become active), saving now will capture the | 565 // background tab (will not become active), saving now will capture the |
550 // state properly. If it does eventually become active, another save will | 566 // state properly. If it does eventually become active, another save will |
551 // be triggered to properly capture the end result. | 567 // be triggered to properly capture the end result. |
552 [self saveSessionImmediately:NO]; | 568 [self saveSessionImmediately:NO]; |
553 | 569 |
554 ++_newTabCount; | 570 ++_newTabCount; |
555 } | 571 } |
556 | 572 |
557 - (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex { | 573 - (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex { |
558 if ([self tabAtIndex:toIndex] == tab) | |
559 return; | |
560 | |
561 DCHECK([_tabRetainer containsObject:tab]); | 574 DCHECK([_tabRetainer containsObject:tab]); |
562 DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX)); | 575 DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX)); |
563 int fromIndex = _webStateList.GetIndexOfWebState(tab.webState); | 576 int fromIndex = _webStateList.GetIndexOfWebState(tab.webState); |
564 _webStateList.MoveWebStateAt(fromIndex, static_cast<int>(toIndex)); | 577 _webStateList.MoveWebStateAt(fromIndex, static_cast<int>(toIndex)); |
565 [_observers tabModel:self didMoveTab:tab fromIndex:fromIndex toIndex:toIndex]; | |
566 } | 578 } |
567 | 579 |
568 - (void)replaceTab:(Tab*)oldTab withTab:(Tab*)newTab { | 580 - (void)replaceTab:(Tab*)oldTab withTab:(Tab*)newTab { |
569 DCHECK([_tabRetainer containsObject:oldTab]); | 581 DCHECK([_tabRetainer containsObject:oldTab]); |
570 DCHECK(![_tabRetainer containsObject:newTab]); | 582 DCHECK(![_tabRetainer containsObject:newTab]); |
571 | 583 |
572 int index = _webStateList.GetIndexOfWebState(oldTab.webState); | 584 int index = _webStateList.GetIndexOfWebState(oldTab.webState); |
573 DCHECK_NE(index, WebStateList::kInvalidIndex); | 585 DCHECK_NE(index, WebStateList::kInvalidIndex); |
574 DCHECK_GE(index, 0); | 586 DCHECK_GE(index, 0); |
575 | 587 |
576 base::scoped_nsobject<Tab> tabSaver([oldTab retain]); | 588 base::scoped_nsobject<Tab> tabSaver([oldTab retain]); |
577 [newTab fetchFavicon]; | |
578 [_tabRetainer removeObject:oldTab]; | 589 [_tabRetainer removeObject:oldTab]; |
579 [_tabRetainer addObject:newTab]; | 590 [_tabRetainer addObject:newTab]; |
580 [newTab setParentTabModel:self]; | 591 [newTab setParentTabModel:self]; |
581 | 592 |
582 _webStateList.ReplaceWebStateAt(index, newTab.webState); | 593 _webStateList.ReplaceWebStateAt(index, newTab.webState); |
583 TabParentingGlobalObserver::GetInstance()->OnTabParented(newTab.webState); | |
584 [_observers tabModel:self | |
585 didReplaceTab:oldTab | |
586 withTab:newTab | |
587 atIndex:static_cast<NSUInteger>(index)]; | |
588 | 594 |
589 if (self.currentTab == oldTab) | 595 if (self.currentTab == oldTab) |
590 [self changeSelectedTabFrom:nil to:newTab persistState:NO]; | 596 [self changeSelectedTabFrom:nil to:newTab persistState:NO]; |
591 | 597 |
592 [oldTab setParentTabModel:nil]; | 598 [oldTab setParentTabModel:nil]; |
593 [oldTab close]; | 599 [oldTab close]; |
594 | |
595 // Record a tab clobber, since swapping tabs bypasses the tab code that would | |
596 // normally log clobbers. | |
597 base::RecordAction(base::UserMetricsAction("MobileTabClobbered")); | |
598 } | 600 } |
599 | 601 |
600 - (void)closeTabAtIndex:(NSUInteger)index { | 602 - (void)closeTabAtIndex:(NSUInteger)index { |
601 DCHECK(index < self.count); | 603 DCHECK(index < self.count); |
602 [self closeTab:[self tabAtIndex:index]]; | 604 [self closeTab:[self tabAtIndex:index]]; |
603 } | 605 } |
604 | 606 |
605 - (void)closeTab:(Tab*)tab { | 607 - (void)closeTab:(Tab*)tab { |
606 // Ensure the tab stays alive long enough for us to send out the | 608 // Ensure the tab stays alive long enough for us to send out the |
607 // notice of its destruction to the delegate. | 609 // notice of its destruction to the delegate. |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 // notification. This avoids various parts of the code getting confused | 748 // notification. This avoids various parts of the code getting confused |
747 // when the current tab isn't in the tab model. | 749 // when the current tab isn't in the tab model. |
748 Tab* savedCurrentTab = _currentTab; | 750 Tab* savedCurrentTab = _currentTab; |
749 if (closedTab == _currentTab) | 751 if (closedTab == _currentTab) |
750 _currentTab.reset(nil); | 752 _currentTab.reset(nil); |
751 | 753 |
752 DCHECK([_tabRetainer containsObject:closedTab]); | 754 DCHECK([_tabRetainer containsObject:closedTab]); |
753 [_tabRetainer removeObject:closedTab]; | 755 [_tabRetainer removeObject:closedTab]; |
754 | 756 |
755 _webStateList.DetachWebStateAt(closedTabIndex); | 757 _webStateList.DetachWebStateAt(closedTabIndex); |
756 [_observers tabModel:self | |
757 didRemoveTab:closedTab | |
758 atIndex:static_cast<NSUInteger>(closedTabIndex)]; | |
759 [_observers tabModelDidChangeTabCount:self]; | |
760 | 758 |
761 // Current tab has closed, update the selected tab and swap in its | 759 // Current tab has closed, update the selected tab and swap in its |
762 // contents. There is nothing to do if a non-selected tab is closed as | 760 // contents. There is nothing to do if a non-selected tab is closed as |
763 // the selection isn't index-based, therefore it hasn't changed. | 761 // the selection isn't index-based, therefore it hasn't changed. |
764 // -changeSelectedTabFrom: will persist the state change, so only do it | 762 // -changeSelectedTabFrom: will persist the state change, so only do it |
765 // if the selection isn't changing. | 763 // if the selection isn't changing. |
766 if (closedTab == savedCurrentTab) { | 764 if (closedTab == savedCurrentTab) { |
767 [self changeSelectedTabFrom:closedTab to:newSelection persistState:NO]; | 765 [self changeSelectedTabFrom:closedTab to:newSelection persistState:NO]; |
768 } else { | 766 } else { |
769 [self saveSessionImmediately:NO]; | 767 [self saveSessionImmediately:NO]; |
770 } | 768 } |
771 base::RecordAction(base::UserMetricsAction("MobileTabClosed")); | |
772 ++_closedTabCount; | 769 ++_closedTabCount; |
773 } | 770 } |
774 | 771 |
775 - (void)navigationCommittedInTab:(Tab*)tab { | 772 - (void)navigationCommittedInTab:(Tab*)tab { |
776 if (self.offTheRecord) | 773 if (self.offTheRecord) |
777 return; | 774 return; |
778 if (![tab navigationManager]) | 775 if (![tab navigationManager]) |
779 return; | 776 return; |
780 | 777 |
781 // See if the navigation was within a page; if so ignore it. | 778 // See if the navigation was within a page; if so ignore it. |
(...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1108 web::NavigationManager::WebLoadParams params(URL); | 1105 web::NavigationManager::WebLoadParams params(URL); |
1109 params.referrer = referrer; | 1106 params.referrer = referrer; |
1110 params.transition_type = ui::PAGE_TRANSITION_TYPED; | 1107 params.transition_type = ui::PAGE_TRANSITION_TYPED; |
1111 [[tab webController] loadWithParams:params]; | 1108 [[tab webController] loadWithParams:params]; |
1112 [tab webController].webUsageEnabled = webUsageEnabled_; | 1109 [tab webController].webUsageEnabled = webUsageEnabled_; |
1113 [self insertTab:tab atIndex:index]; | 1110 [self insertTab:tab atIndex:index]; |
1114 return tab; | 1111 return tab; |
1115 } | 1112 } |
1116 | 1113 |
1117 @end | 1114 @end |
OLD | NEW |