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 |