Chromium Code Reviews| 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..e6f4579d22fb35440773676484588ef1d487bfda 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,47 @@ 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_); |
| + |
| + using std::swap; |
|
rohitrao (ping after 24h)
2017/02/22 14:57:53
Why using? Could you just call std::swap(web_stat
sdefresne
2017/02/22 18:23:47
This is an habit that I grew that allows to force
|
| + 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 +116,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 +173,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); |
| + FixOpenersReferencing(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 +192,8 @@ web::WebState* WebStateList::ReplaceWebStateAt(int index, |
| void WebStateList::DetachWebStateAt(int index) { |
| DCHECK(ContainsIndex(index)); |
| + FixOpenersReferencing(index); |
| + |
| web::WebState* web_state = web_state_wrappers_[index]->web_state(); |
| web_state_wrappers_.erase(web_state_wrappers_.begin() + index); |
| @@ -119,5 +209,38 @@ void WebStateList::RemoveObserver(WebStateListObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| +void WebStateList::FixOpenersReferencing(int index) { |
|
rohitrao (ping after 24h)
2017/02/22 14:57:52
Is this really ClearOpenersReferencing()?
sdefresne
2017/02/22 18:23:47
Yes. I've named it this following TabStripModel (b
|
| + 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; |