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/shared/chrome/browser/tabs/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/shared/chrome/browser/tabs/web_state_list_delegate.h" |
| 13 #import "ios/shared/chrome/browser/tabs/web_state_list_observer.h" |
| 14 #import "ios/shared/chrome/browser/tabs/web_state_list_order_controller.h" |
| 15 #import "ios/shared/chrome/browser/tabs/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 |