| 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; | 
| } | 
|  | 
|  |