| OLD | NEW |
| (Empty) |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "ios/chrome/browser/web_state_list/web_state_list.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #import "ios/chrome/browser/web_state_list/web_state_list_delegate.h" | |
| 13 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h" | |
| 14 #import "ios/chrome/browser/web_state_list/web_state_list_order_controller.h" | |
| 15 #import "ios/chrome/browser/web_state_list/web_state_opener.h" | |
| 16 #import "ios/web/public/navigation_manager.h" | |
| 17 #import "ios/web/public/web_state/web_state.h" | |
| 18 | |
| 19 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
| 20 #error "This file requires ARC support." | |
| 21 #endif | |
| 22 | |
| 23 // Wrapper around a WebState stored in a WebStateList. | |
| 24 class WebStateList::WebStateWrapper { | |
| 25 public: | |
| 26 explicit WebStateWrapper(std::unique_ptr<web::WebState> web_state); | |
| 27 ~WebStateWrapper(); | |
| 28 | |
| 29 web::WebState* web_state() const { return web_state_.get(); } | |
| 30 | |
| 31 // Replaces the wrapped WebState (and clear associated state) and returns the | |
| 32 // old WebState after forfeiting ownership. | |
| 33 std::unique_ptr<web::WebState> ReplaceWebState( | |
| 34 std::unique_ptr<web::WebState> web_state); | |
| 35 | |
| 36 // Gets and sets information about this WebState opener. The navigation index | |
| 37 // is used to detect navigation changes during the same session. | |
| 38 WebStateOpener opener() const { return opener_; } | |
| 39 void set_opener(WebStateOpener opener) { opener_ = opener; } | |
| 40 | |
| 41 // Returns whether |opener| spawned the wrapped WebState. If |use_group| is | |
| 42 // true, also use the opener navigation index to detect navigation changes | |
| 43 // during the same session. | |
| 44 bool WasOpenedBy(const web::WebState* opener, | |
| 45 int opener_navigation_index, | |
| 46 bool use_group) const; | |
| 47 | |
| 48 private: | |
| 49 std::unique_ptr<web::WebState> web_state_; | |
| 50 WebStateOpener opener_; | |
| 51 | |
| 52 DISALLOW_COPY_AND_ASSIGN(WebStateWrapper); | |
| 53 }; | |
| 54 | |
| 55 WebStateList::WebStateWrapper::WebStateWrapper( | |
| 56 std::unique_ptr<web::WebState> web_state) | |
| 57 : web_state_(std::move(web_state)), opener_(nullptr) { | |
| 58 DCHECK(web_state_); | |
| 59 } | |
| 60 | |
| 61 WebStateList::WebStateWrapper::~WebStateWrapper() = default; | |
| 62 | |
| 63 std::unique_ptr<web::WebState> WebStateList::WebStateWrapper::ReplaceWebState( | |
| 64 std::unique_ptr<web::WebState> web_state) { | |
| 65 DCHECK_NE(web_state.get(), web_state_.get()); | |
| 66 std::swap(web_state, web_state_); | |
| 67 opener_ = WebStateOpener(nullptr); | |
| 68 return web_state; | |
| 69 } | |
| 70 | |
| 71 bool WebStateList::WebStateWrapper::WasOpenedBy(const web::WebState* opener, | |
| 72 int opener_navigation_index, | |
| 73 bool use_group) const { | |
| 74 DCHECK(opener); | |
| 75 if (opener_.opener != opener) | |
| 76 return false; | |
| 77 | |
| 78 if (!use_group) | |
| 79 return true; | |
| 80 | |
| 81 return opener_.navigation_index == opener_navigation_index; | |
| 82 } | |
| 83 | |
| 84 WebStateList::WebStateList(WebStateListDelegate* delegate) | |
| 85 : delegate_(delegate), | |
| 86 order_controller_(base::MakeUnique<WebStateListOrderController>(this)) { | |
| 87 DCHECK(delegate_); | |
| 88 } | |
| 89 | |
| 90 WebStateList::~WebStateList() { | |
| 91 CloseAllWebStates(); | |
| 92 } | |
| 93 | |
| 94 bool WebStateList::ContainsIndex(int index) const { | |
| 95 return 0 <= index && index < count(); | |
| 96 } | |
| 97 | |
| 98 web::WebState* WebStateList::GetActiveWebState() const { | |
| 99 if (active_index_ != kInvalidIndex) | |
| 100 return GetWebStateAt(active_index_); | |
| 101 return nullptr; | |
| 102 } | |
| 103 | |
| 104 web::WebState* WebStateList::GetWebStateAt(int index) const { | |
| 105 DCHECK(ContainsIndex(index)); | |
| 106 return web_state_wrappers_[index]->web_state(); | |
| 107 } | |
| 108 | |
| 109 int WebStateList::GetIndexOfWebState(const web::WebState* web_state) const { | |
| 110 for (int index = 0; index < count(); ++index) { | |
| 111 if (web_state_wrappers_[index]->web_state() == web_state) | |
| 112 return index; | |
| 113 } | |
| 114 return kInvalidIndex; | |
| 115 } | |
| 116 | |
| 117 WebStateOpener WebStateList::GetOpenerOfWebStateAt(int index) const { | |
| 118 DCHECK(ContainsIndex(index)); | |
| 119 return web_state_wrappers_[index]->opener(); | |
| 120 } | |
| 121 | |
| 122 void WebStateList::SetOpenerOfWebStateAt(int index, WebStateOpener opener) { | |
| 123 DCHECK(ContainsIndex(index)); | |
| 124 DCHECK(ContainsIndex(GetIndexOfWebState(opener.opener))); | |
| 125 web_state_wrappers_[index]->set_opener(opener); | |
| 126 } | |
| 127 | |
| 128 int WebStateList::GetIndexOfNextWebStateOpenedBy(const web::WebState* opener, | |
| 129 int start_index, | |
| 130 bool use_group) const { | |
| 131 return GetIndexOfNthWebStateOpenedBy(opener, start_index, use_group, 1); | |
| 132 } | |
| 133 | |
| 134 int WebStateList::GetIndexOfLastWebStateOpenedBy(const web::WebState* opener, | |
| 135 int start_index, | |
| 136 bool use_group) const { | |
| 137 return GetIndexOfNthWebStateOpenedBy(opener, start_index, use_group, INT_MAX); | |
| 138 } | |
| 139 | |
| 140 void WebStateList::InsertWebState(int index, | |
| 141 std::unique_ptr<web::WebState> web_state) { | |
| 142 DCHECK(ContainsIndex(index) || index == count()); | |
| 143 delegate_->WillAddWebState(web_state.get()); | |
| 144 | |
| 145 web::WebState* web_state_ptr = web_state.get(); | |
| 146 web_state_wrappers_.insert( | |
| 147 web_state_wrappers_.begin() + index, | |
| 148 base::MakeUnique<WebStateWrapper>(std::move(web_state))); | |
| 149 | |
| 150 if (active_index_ >= index) | |
| 151 ++active_index_; | |
| 152 | |
| 153 for (auto& observer : observers_) | |
| 154 observer.WebStateInsertedAt(this, web_state_ptr, index); | |
| 155 } | |
| 156 | |
| 157 void WebStateList::AppendWebState(ui::PageTransition transition, | |
| 158 std::unique_ptr<web::WebState> web_state, | |
| 159 WebStateOpener opener) { | |
| 160 int index = | |
| 161 order_controller_->DetermineInsertionIndex(transition, opener.opener); | |
| 162 if (index < 0 || count() < index) | |
| 163 index = count(); | |
| 164 | |
| 165 InsertWebState(index, std::move(web_state)); | |
| 166 | |
| 167 if (opener.opener) | |
| 168 SetOpenerOfWebStateAt(index, opener); | |
| 169 } | |
| 170 | |
| 171 void WebStateList::MoveWebStateAt(int from_index, int to_index) { | |
| 172 DCHECK(ContainsIndex(from_index)); | |
| 173 DCHECK(ContainsIndex(to_index)); | |
| 174 if (from_index == to_index) | |
| 175 return; | |
| 176 | |
| 177 std::unique_ptr<WebStateWrapper> web_state_wrapper = | |
| 178 std::move(web_state_wrappers_[from_index]); | |
| 179 web::WebState* web_state = web_state_wrapper->web_state(); | |
| 180 web_state_wrappers_.erase(web_state_wrappers_.begin() + from_index); | |
| 181 web_state_wrappers_.insert(web_state_wrappers_.begin() + to_index, | |
| 182 std::move(web_state_wrapper)); | |
| 183 | |
| 184 if (active_index_ == from_index) { | |
| 185 active_index_ = to_index; | |
| 186 } else { | |
| 187 int min = std::min(from_index, to_index); | |
| 188 int max = std::max(from_index, to_index); | |
| 189 int delta = from_index < to_index ? -1 : +1; | |
| 190 if (min <= active_index_ && active_index_ <= max) | |
| 191 active_index_ += delta; | |
| 192 } | |
| 193 | |
| 194 for (auto& observer : observers_) | |
| 195 observer.WebStateMoved(this, web_state, from_index, to_index); | |
| 196 } | |
| 197 | |
| 198 std::unique_ptr<web::WebState> WebStateList::ReplaceWebStateAt( | |
| 199 int index, | |
| 200 std::unique_ptr<web::WebState> web_state) { | |
| 201 DCHECK(ContainsIndex(index)); | |
| 202 delegate_->WillAddWebState(web_state.get()); | |
| 203 | |
| 204 ClearOpenersReferencing(index); | |
| 205 | |
| 206 web::WebState* web_state_ptr = web_state.get(); | |
| 207 std::unique_ptr<web::WebState> old_web_state = | |
| 208 web_state_wrappers_[index]->ReplaceWebState(std::move(web_state)); | |
| 209 | |
| 210 for (auto& observer : observers_) | |
| 211 observer.WebStateReplacedAt(this, old_web_state.get(), web_state_ptr, | |
| 212 index); | |
| 213 | |
| 214 delegate_->WebStateDetached(old_web_state.get()); | |
| 215 return old_web_state; | |
| 216 } | |
| 217 | |
| 218 std::unique_ptr<web::WebState> WebStateList::DetachWebStateAt(int index) { | |
| 219 DCHECK(ContainsIndex(index)); | |
| 220 int new_active_index = order_controller_->DetermineNewActiveIndex(index); | |
| 221 | |
| 222 web::WebState* web_state = web_state_wrappers_[index]->web_state(); | |
| 223 for (auto& observer : observers_) | |
| 224 observer.WillDetachWebStateAt(this, web_state, index); | |
| 225 | |
| 226 ClearOpenersReferencing(index); | |
| 227 std::unique_ptr<web::WebState> detached_web_state = | |
| 228 web_state_wrappers_[index]->ReplaceWebState(nullptr); | |
| 229 web_state_wrappers_.erase(web_state_wrappers_.begin() + index); | |
| 230 | |
| 231 // Update the active index to prevent observer from seeing an invalid WebState | |
| 232 // as the active one but only send the WebStateActivatedAt notification after | |
| 233 // the WebStateDetachedAt one. | |
| 234 bool active_web_state_was_closed = (index == active_index_); | |
| 235 if (active_index_ > index) | |
| 236 --active_index_; | |
| 237 else if (active_index_ == index) | |
| 238 active_index_ = new_active_index; | |
| 239 | |
| 240 for (auto& observer : observers_) | |
| 241 observer.WebStateDetachedAt(this, web_state, index); | |
| 242 | |
| 243 if (active_web_state_was_closed) | |
| 244 NotifyIfActiveWebStateChanged(web_state, false); | |
| 245 | |
| 246 delegate_->WebStateDetached(web_state); | |
| 247 return detached_web_state; | |
| 248 } | |
| 249 | |
| 250 void WebStateList::CloseWebStateAt(int index) { | |
| 251 auto detached_web_state = DetachWebStateAt(index); | |
| 252 | |
| 253 for (auto& observer : observers_) | |
| 254 observer.WillCloseWebStateAt(this, detached_web_state.get(), index); | |
| 255 | |
| 256 detached_web_state.reset(); | |
| 257 } | |
| 258 | |
| 259 void WebStateList::CloseAllWebStates() { | |
| 260 while (!empty()) | |
| 261 CloseWebStateAt(count() - 1); | |
| 262 } | |
| 263 | |
| 264 void WebStateList::ActivateWebStateAt(int index) { | |
| 265 DCHECK(ContainsIndex(index)); | |
| 266 web::WebState* old_web_state = GetActiveWebState(); | |
| 267 active_index_ = index; | |
| 268 NotifyIfActiveWebStateChanged(old_web_state, true); | |
| 269 } | |
| 270 | |
| 271 void WebStateList::AddObserver(WebStateListObserver* observer) { | |
| 272 observers_.AddObserver(observer); | |
| 273 } | |
| 274 | |
| 275 void WebStateList::RemoveObserver(WebStateListObserver* observer) { | |
| 276 observers_.RemoveObserver(observer); | |
| 277 } | |
| 278 | |
| 279 void WebStateList::ClearOpenersReferencing(int index) { | |
| 280 web::WebState* old_web_state = web_state_wrappers_[index]->web_state(); | |
| 281 for (auto& web_state_wrapper : web_state_wrappers_) { | |
| 282 if (web_state_wrapper->opener().opener == old_web_state) | |
| 283 web_state_wrapper->set_opener(WebStateOpener(nullptr)); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 void WebStateList::NotifyIfActiveWebStateChanged(web::WebState* old_web_state, | |
| 288 bool user_action) { | |
| 289 web::WebState* new_web_state = GetActiveWebState(); | |
| 290 if (old_web_state == new_web_state) | |
| 291 return; | |
| 292 | |
| 293 for (auto& observer : observers_) { | |
| 294 observer.WebStateActivatedAt(this, old_web_state, new_web_state, | |
| 295 active_index_, user_action); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 int WebStateList::GetIndexOfNthWebStateOpenedBy(const web::WebState* opener, | |
| 300 int start_index, | |
| 301 bool use_group, | |
| 302 int n) const { | |
| 303 DCHECK_GT(n, 0); | |
| 304 if (!opener || !ContainsIndex(start_index) || start_index == INT_MAX) | |
| 305 return kInvalidIndex; | |
| 306 | |
| 307 const int opener_navigation_index = | |
| 308 use_group ? opener->GetNavigationManager()->GetLastCommittedItemIndex() | |
| 309 : -1; | |
| 310 | |
| 311 int found_index = kInvalidIndex; | |
| 312 for (int index = start_index + 1; index < count() && n; ++index) { | |
| 313 if (web_state_wrappers_[index]->WasOpenedBy(opener, opener_navigation_index, | |
| 314 use_group)) { | |
| 315 found_index = index; | |
| 316 --n; | |
| 317 } else if (found_index != kInvalidIndex) { | |
| 318 return found_index; | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 return found_index; | |
| 323 } | |
| 324 | |
| 325 // static | |
| 326 const int WebStateList::kInvalidIndex; | |
| OLD | NEW |