| Index: ios/shared/chrome/browser/tabs/web_state_list.mm | 
| diff --git a/ios/shared/chrome/browser/tabs/web_state_list.mm b/ios/shared/chrome/browser/tabs/web_state_list.mm | 
| index cb85ed433ee72233257fc0c3181adf4e9dfaca74..283ddeed94cf9d66f914727c8c0bb15f36218bc3 100644 | 
| --- a/ios/shared/chrome/browser/tabs/web_state_list.mm | 
| +++ b/ios/shared/chrome/browser/tabs/web_state_list.mm | 
| @@ -4,9 +4,12 @@ | 
|  | 
| #import "ios/shared/chrome/browser/tabs/web_state_list.h" | 
|  | 
| +#include <utility> | 
| + | 
| #include "base/logging.h" | 
| #include "base/memory/ptr_util.h" | 
| #import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" | 
| +#import "ios/web/public/navigation_manager.h" | 
| #import "ios/web/public/web_state/web_state.h" | 
|  | 
| #if !defined(__has_feature) || !__has_feature(objc_arc) | 
| @@ -22,10 +25,27 @@ class WebStateList::WebStateWrapper { | 
| ~WebStateWrapper(); | 
|  | 
| web::WebState* web_state() const { return web_state_; } | 
| -  void set_web_state(web::WebState* web_state) { web_state_ = web_state; } | 
| +  web::WebState* opener() const { return opener_; } | 
| + | 
| +  // Replaces the wrapped WebState (and clear associated state) and returns the | 
| +  // old WebState after forfeiting ownership. | 
| +  web::WebState* ReplaceWebState(web::WebState* web_state); | 
| + | 
| +  // Sets the opener for the wrapped WebState and record the opener navigation | 
| +  // index to allow detecting navigation changes during the same session. | 
| +  void SetOpener(web::WebState* opener); | 
| + | 
| +  // Returns whether |opener| spawned the wrapped WebState. If |use_group| is | 
| +  // true, also use the opener navigation index to detect navigation changes | 
| +  // during the same session. | 
| +  bool WasOpenedBy(const web::WebState* opener, | 
| +                   int opener_navigation_index, | 
| +                   bool use_group) const; | 
|  | 
| private: | 
| web::WebState* web_state_; | 
| +  web::WebState* opener_ = nullptr; | 
| +  int opener_last_committed_index_; | 
| const bool has_web_state_ownership_; | 
|  | 
| DISALLOW_COPY_AND_ASSIGN(WebStateWrapper); | 
| @@ -33,13 +53,45 @@ class WebStateList::WebStateWrapper { | 
|  | 
| WebStateList::WebStateWrapper::WebStateWrapper(web::WebState* web_state, | 
| bool assume_ownership) | 
| -    : web_state_(web_state), has_web_state_ownership_(assume_ownership) {} | 
| +    : web_state_(web_state), has_web_state_ownership_(assume_ownership) { | 
| +  DCHECK(web_state_); | 
| +} | 
|  | 
| WebStateList::WebStateWrapper::~WebStateWrapper() { | 
| if (has_web_state_ownership_) | 
| delete web_state_; | 
| } | 
|  | 
| +web::WebState* WebStateList::WebStateWrapper::ReplaceWebState( | 
| +    web::WebState* web_state) { | 
| +  DCHECK(web_state); | 
| +  DCHECK_NE(web_state, web_state_); | 
| +  std::swap(web_state, web_state_); | 
| +  opener_ = nullptr; | 
| +  return web_state; | 
| +} | 
| + | 
| +void WebStateList::WebStateWrapper::SetOpener(web::WebState* opener) { | 
| +  opener_ = opener; | 
| +  if (opener_) { | 
| +    opener_last_committed_index_ = | 
| +        opener_->GetNavigationManager()->GetLastCommittedItemIndex(); | 
| +  } | 
| +} | 
| + | 
| +bool WebStateList::WebStateWrapper::WasOpenedBy(const web::WebState* opener, | 
| +                                                int opener_navigation_index, | 
| +                                                bool use_group) const { | 
| +  DCHECK(opener); | 
| +  if (opener_ != opener) | 
| +    return false; | 
| + | 
| +  if (!use_group) | 
| +    return true; | 
| + | 
| +  return opener_last_committed_index_ == opener_navigation_index; | 
| +} | 
| + | 
| WebStateList::WebStateList(WebStateOwnership ownership) | 
| : web_state_ownership_(ownership) {} | 
|  | 
| @@ -62,13 +114,41 @@ int WebStateList::GetIndexOfWebState(const web::WebState* web_state) const { | 
| return kInvalidIndex; | 
| } | 
|  | 
| -void WebStateList::InsertWebState(int index, web::WebState* web_state) { | 
| +web::WebState* WebStateList::GetOpenerOfWebStateAt(int index) const { | 
| +  DCHECK(ContainsIndex(index)); | 
| +  return web_state_wrappers_[index]->opener(); | 
| +} | 
| + | 
| +void WebStateList::SetOpenerOfWebStateAt(int index, web::WebState* opener) { | 
| +  DCHECK(ContainsIndex(index)); | 
| +  DCHECK(ContainsIndex(GetIndexOfWebState(opener))); | 
| +  web_state_wrappers_[index]->SetOpener(opener); | 
| +} | 
| + | 
| +int WebStateList::GetIndexOfNextWebStateOpenedBy(const web::WebState* opener, | 
| +                                                 int start_index, | 
| +                                                 bool use_group) const { | 
| +  return GetIndexOfNthWebStateOpenedBy(opener, start_index, use_group, 1); | 
| +} | 
| + | 
| +int WebStateList::GetIndexOfLastWebStateOpenedBy(const web::WebState* opener, | 
| +                                                 int start_index, | 
| +                                                 bool use_group) const { | 
| +  return GetIndexOfNthWebStateOpenedBy(opener, start_index, use_group, INT_MAX); | 
| +} | 
| + | 
| +void WebStateList::InsertWebState(int index, | 
| +                                  web::WebState* web_state, | 
| +                                  web::WebState* opener) { | 
| DCHECK(ContainsIndex(index) || index == count()); | 
| web_state_wrappers_.insert( | 
| web_state_wrappers_.begin() + index, | 
| base::MakeUnique<WebStateWrapper>(web_state, | 
| web_state_ownership_ == WebStateOwned)); | 
|  | 
| +  if (opener) | 
| +    SetOpenerOfWebStateAt(index, opener); | 
| + | 
| for (auto& observer : observers_) | 
| observer.WebStateInsertedAt(this, web_state, index); | 
| } | 
| @@ -91,10 +171,16 @@ void WebStateList::MoveWebStateAt(int from_index, int to_index) { | 
| } | 
|  | 
| web::WebState* WebStateList::ReplaceWebStateAt(int index, | 
| -                                               web::WebState* web_state) { | 
| +                                               web::WebState* web_state, | 
| +                                               web::WebState* opener) { | 
| DCHECK(ContainsIndex(index)); | 
| -  web::WebState* old_web_state = web_state_wrappers_[index]->web_state(); | 
| -  web_state_wrappers_[index]->set_web_state(web_state); | 
| +  ClearOpenersReferencing(index); | 
| + | 
| +  auto& web_state_wrapper = web_state_wrappers_[index]; | 
| +  web::WebState* old_web_state = web_state_wrapper->ReplaceWebState(web_state); | 
| + | 
| +  if (opener && opener != old_web_state) | 
| +    SetOpenerOfWebStateAt(index, opener); | 
|  | 
| for (auto& observer : observers_) | 
| observer.WebStateReplacedAt(this, old_web_state, web_state, index); | 
| @@ -104,6 +190,8 @@ web::WebState* WebStateList::ReplaceWebStateAt(int index, | 
|  | 
| void WebStateList::DetachWebStateAt(int index) { | 
| DCHECK(ContainsIndex(index)); | 
| +  ClearOpenersReferencing(index); | 
| + | 
| web::WebState* web_state = web_state_wrappers_[index]->web_state(); | 
| web_state_wrappers_.erase(web_state_wrappers_.begin() + index); | 
|  | 
| @@ -119,5 +207,38 @@ void WebStateList::RemoveObserver(WebStateListObserver* observer) { | 
| observers_.RemoveObserver(observer); | 
| } | 
|  | 
| +void WebStateList::ClearOpenersReferencing(int index) { | 
| +  web::WebState* old_web_state = web_state_wrappers_[index]->web_state(); | 
| +  for (auto& web_state_wrapper : web_state_wrappers_) { | 
| +    if (web_state_wrapper->opener() == old_web_state) | 
| +      web_state_wrapper->SetOpener(nullptr); | 
| +  } | 
| +} | 
| + | 
| +int WebStateList::GetIndexOfNthWebStateOpenedBy(const web::WebState* opener, | 
| +                                                int start_index, | 
| +                                                bool use_group, | 
| +                                                int n) const { | 
| +  DCHECK_GT(n, 0); | 
| +  if (!opener || !ContainsIndex(start_index) || start_index == INT_MAX) | 
| +    return kInvalidIndex; | 
| + | 
| +  const int opener_navigation_index = | 
| +      use_group ? opener->GetNavigationManager()->GetCurrentItemIndex() : -1; | 
| + | 
| +  int found_index = kInvalidIndex; | 
| +  for (int index = start_index + 1; index < count() && n; ++index) { | 
| +    if (web_state_wrappers_[index]->WasOpenedBy(opener, opener_navigation_index, | 
| +                                                use_group)) { | 
| +      found_index = index; | 
| +      --n; | 
| +    } else if (found_index != kInvalidIndex) { | 
| +      return found_index; | 
| +    } | 
| +  } | 
| + | 
| +  return found_index; | 
| +} | 
| + | 
| // static | 
| const int WebStateList::kInvalidIndex; | 
|  |