Index: ios/chrome/browser/tabs/tab_model.mm |
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm |
index 65d18358c971db75aebe3552ece1f4bbf88df1a3..732ce153e113b5dad4160388e75391ae2533ca29 100644 |
--- a/ios/chrome/browser/tabs/tab_model.mm |
+++ b/ios/chrome/browser/tabs/tab_model.mm |
@@ -117,6 +117,23 @@ void CleanCertificatePolicyCache( |
base::Unretained(web_state_list))); |
} |
+// Internal helper function returning the opener for a given Tab by |
+// checking the associated Tab tabId (should be removed once the opener |
+// is passed to the insertTab:atIndex: and replaceTab:withTab: methods). |
+Tab* GetOpenerForTab(id<NSFastEnumeration> tabs, Tab* tab) { |
+ NSString* opener_id = |
+ [tab navigationManager]->GetSessionController().openerId; |
+ if (!opener_id) |
+ return nullptr; |
+ |
+ for (Tab* currentTab in tabs) { |
+ if ([opener_id isEqualToString:currentTab.tabId]) |
+ return currentTab; |
+ } |
+ |
+ return nullptr; |
+} |
+ |
} // anonymous namespace |
@interface TabModelWebStateProxyFactory : NSObject<WebStateProxyFactory> |
@@ -217,6 +234,12 @@ void CleanCertificatePolicyCache( |
- (BOOL)restoreSessionWindow:(SessionWindowIOS*)window |
persistState:(BOOL)persistState; |
+// Helper method to insert |tab| at the given |index| recording |parentTab| as |
+// the opener. Broadcasts the proper notifications about the change. The |
+// receiver should be set as the parentTabModel for |tab|; this method doesn't |
+// check that. |
+- (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index opener:(Tab*)parentTab; |
+ |
@end |
@implementation TabModel |
@@ -405,68 +428,43 @@ void CleanCertificatePolicyCache( |
} |
- (Tab*)nextTabWithOpener:(Tab*)tab afterTab:(Tab*)afterTab { |
- NSUInteger startIndex = NSNotFound; |
- // Start looking after |afterTab|. If it's not found, start looking after |
- // |tab|. If it's not found either, bail. |
+ int startIndex = WebStateList::kInvalidIndex; |
if (afterTab) |
- startIndex = [self indexOfTab:afterTab]; |
- if (startIndex == NSNotFound) |
- startIndex = [self indexOfTab:tab]; |
- if (startIndex == NSNotFound) |
+ startIndex = _webStateList.GetIndexOfWebState(afterTab.webState); |
+ |
+ if (startIndex == WebStateList::kInvalidIndex) |
+ startIndex = _webStateList.GetIndexOfWebState(tab.webState); |
+ |
+ const int index = _webStateList.GetIndexOfNextWebStateOpenedBy( |
+ tab.webState, startIndex, false); |
+ if (index == WebStateList::kInvalidIndex) |
return nil; |
- NSString* parentID = tab.tabId; |
- for (NSUInteger i = startIndex + 1; i < self.count; ++i) { |
- Tab* current = [self tabAtIndex:i]; |
- DCHECK([current navigationManager]); |
- CRWSessionController* sessionController = |
- [current navigationManager]->GetSessionController(); |
- if ([sessionController.openerId isEqualToString:parentID]) |
- return current; |
- } |
- return nil; |
+ |
+ DCHECK_GE(index, 0); |
+ return [self tabAtIndex:static_cast<NSUInteger>(index)]; |
} |
- (Tab*)lastTabWithOpener:(Tab*)tab { |
- NSUInteger startIndex = [self indexOfTab:tab]; |
- if (startIndex == NSNotFound) |
+ int startIndex = _webStateList.GetIndexOfWebState(tab.webState); |
+ if (startIndex == WebStateList::kInvalidIndex) |
return nil; |
- // There is at least one tab in the model, because otherwise the above check |
- // would have returned. |
- NSString* parentID = tab.tabId; |
- DCHECK([tab navigationManager]); |
- NSInteger parentNavIndex = [tab navigationManager]->GetCurrentItemIndex(); |
- |
- Tab* match = nil; |
- // Find the last tab in the first matching 'group'. A 'group' is a set of |
- // tabs whose opener's id and opener's navigation index match. The navigation |
- // index is used in addition to the session id to detect navigations changes |
- // within the same session. |
- for (NSUInteger i = startIndex + 1; i < self.count; ++i) { |
- Tab* tabToCheck = [self tabAtIndex:i]; |
- DCHECK([tabToCheck navigationManager]); |
- CRWSessionController* sessionController = |
- [tabToCheck navigationManager]->GetSessionController(); |
- if ([sessionController.openerId isEqualToString:parentID] && |
- sessionController.openerNavigationIndex == parentNavIndex) { |
- match = tabToCheck; |
- } else if (match) { |
- break; |
- } |
- } |
- return match; |
+ |
+ const int index = _webStateList.GetIndexOfLastWebStateOpenedBy( |
+ tab.webState, startIndex, true); |
+ if (index == WebStateList::kInvalidIndex) |
+ return nil; |
+ |
+ DCHECK_GE(index, 0); |
+ return [self tabAtIndex:static_cast<NSUInteger>(index)]; |
} |
- (Tab*)openerOfTab:(Tab*)tab { |
- if (![tab navigationManager]) |
- return nil; |
- NSString* openerId = [tab navigationManager]->GetSessionController().openerId; |
- if (!openerId.length) // Short-circuit if opener is empty. |
+ int index = _webStateList.GetIndexOfWebState(tab.webState); |
+ if (index == WebStateList::kInvalidIndex) |
return nil; |
- for (Tab* iteratedTab in self) { |
- if ([iteratedTab.tabId isEqualToString:openerId]) |
- return iteratedTab; |
- } |
- return nil; |
+ |
+ web::WebState* opener = _webStateList.GetOpenerOfWebStateAt(index); |
+ return opener ? LegacyTabHelper::GetTabForWebState(opener) : nil; |
} |
- (Tab*)insertOrUpdateTabWithURL:(const GURL&)URL |
@@ -553,13 +551,14 @@ void CleanCertificatePolicyCache( |
return tab; |
} |
-- (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index { |
+- (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index opener:(Tab*)parentTab { |
DCHECK(tab); |
DCHECK(![_tabRetainer containsObject:tab]); |
DCHECK_LE(index, static_cast<NSUInteger>(INT_MAX)); |
[_tabRetainer addObject:tab]; |
- _webStateList.InsertWebState(static_cast<int>(index), tab.webState); |
+ _webStateList.InsertWebState(static_cast<int>(index), tab.webState, |
+ parentTab.webState); |
// Persist the session due to a new tab being inserted. If this is a |
// background tab (will not become active), saving now will capture the |
@@ -570,6 +569,10 @@ void CleanCertificatePolicyCache( |
++_newTabCount; |
} |
+- (void)insertTab:(Tab*)tab atIndex:(NSUInteger)index { |
+ [self insertTab:tab atIndex:index opener:GetOpenerForTab(self, tab)]; |
+} |
+ |
- (void)moveTab:(Tab*)tab toIndex:(NSUInteger)toIndex { |
DCHECK([_tabRetainer containsObject:tab]); |
DCHECK_LE(toIndex, static_cast<NSUInteger>(INT_MAX)); |
@@ -590,7 +593,8 @@ void CleanCertificatePolicyCache( |
[_tabRetainer addObject:newTab]; |
[newTab setParentTabModel:self]; |
- _webStateList.ReplaceWebStateAt(index, newTab.webState); |
+ _webStateList.ReplaceWebStateAt(index, newTab.webState, |
+ GetOpenerForTab(self, newTab).webState); |
if (self.currentTab == oldTab) |
[self changeSelectedTabFrom:nil to:newTab persistState:NO]; |
@@ -889,7 +893,7 @@ void CleanCertificatePolicyCache( |
// TODO(crbug.com/661988): Make this work. |
} |
- [self insertTab:tab atIndex:index]; |
+ [self insertTab:tab atIndex:index opener:parentTab]; |
if (!inBackground && _tabUsageRecorder) |
_tabUsageRecorder->TabCreatedForSelection(tab); |
@@ -986,20 +990,43 @@ void CleanCertificatePolicyCache( |
scoped_refptr<web::CertificatePolicyCache> policyCache = |
web::BrowserState::GetCertificatePolicyCache(_browserState); |
+ base::scoped_nsobject<NSMutableArray<Tab*>> restoredTabs( |
+ [[NSMutableArray alloc] initWithCapacity:sessions.count]); |
+ |
+ // Recreate all the restored Tabs and add them to the WebStateList without |
+ // any opener-opened relationship (as the n-th restored Tab opener may be |
+ // at an index larger than n). Then in a second pass fix the openers. |
for (CRWSessionStorage* session in sessions) { |
std::unique_ptr<web::WebState> webState = |
web::WebState::Create(params, session); |
- DCHECK_EQ(webState->GetBrowserState(), _browserState); |
- Tab* tab = |
- [self insertTabWithWebState:std::move(webState) atIndex:self.count]; |
- tab.webController.usePlaceholderOverlay = YES; |
+ base::scoped_nsobject<Tab> tab( |
+ [[Tab alloc] initWithWebState:std::move(webState) model:self]); |
+ [tab webController].webUsageEnabled = webUsageEnabled_; |
+ [tab webController].usePlaceholderOverlay = YES; |
// Restore the CertificatePolicyCache (note that webState is invalid after |
// passing it via move semantic to -insertTabWithWebState:atIndex:). |
- UpdateCertificatePolicyCacheFromWebState(policyCache, tab.webState); |
+ UpdateCertificatePolicyCacheFromWebState(policyCache, [tab webState]); |
+ [self insertTab:tab atIndex:self.count opener:nil]; |
+ [restoredTabs addObject:tab.get()]; |
} |
+ |
+ DCHECK_EQ(sessions.count, [restoredTabs count]); |
DCHECK_GT(_webStateList.count(), oldCount); |
+ // Fix openers now that all Tabs have been restored. Only look for an opener |
+ // Tab in the newly restored Tabs and not in the already open Tabs. |
+ for (int index = oldCount; index < _webStateList.count(); ++index) { |
+ DCHECK_GE(index, oldCount); |
+ NSUInteger tabIndex = static_cast<NSUInteger>(index - oldCount); |
+ Tab* tab = [restoredTabs objectAtIndex:tabIndex]; |
+ Tab* opener = GetOpenerForTab(restoredTabs.get(), tab); |
+ if (opener) { |
+ DCHECK(opener.webState); |
+ _webStateList.SetOpenerOfWebStateAt(index, opener.webState); |
+ } |
+ } |
+ |
// Update the selected tab if there was a selected Tab in the saved session. |
if (window.selectedIndex != NSNotFound) { |
NSUInteger selectedIndex = window.selectedIndex + oldCount; |
@@ -1107,7 +1134,7 @@ void CleanCertificatePolicyCache( |
params.transition_type = ui::PAGE_TRANSITION_TYPED; |
[[tab webController] loadWithParams:params]; |
[tab webController].webUsageEnabled = webUsageEnabled_; |
- [self insertTab:tab atIndex:index]; |
+ [self insertTab:tab atIndex:index opener:parentTab]; |
return tab; |
} |