OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 #include "mojo/services/public/cpp/view_manager/view.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "mojo/public/cpp/application/service_provider_impl.h" | |
10 #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" | |
11 #include "mojo/services/public/cpp/view_manager/lib/view_private.h" | |
12 #include "mojo/services/public/cpp/view_manager/view_observer.h" | |
13 #include "mojo/services/public/cpp/view_manager/view_tracker.h" | |
14 | |
15 namespace mojo { | |
16 | |
17 namespace { | |
18 | |
19 void NotifyViewTreeChangeAtReceiver( | |
20 View* receiver, | |
21 const ViewObserver::TreeChangeParams& params, | |
22 bool change_applied) { | |
23 ViewObserver::TreeChangeParams local_params = params; | |
24 local_params.receiver = receiver; | |
25 if (change_applied) { | |
26 FOR_EACH_OBSERVER(ViewObserver, | |
27 *ViewPrivate(receiver).observers(), | |
28 OnTreeChanged(local_params)); | |
29 } else { | |
30 FOR_EACH_OBSERVER(ViewObserver, | |
31 *ViewPrivate(receiver).observers(), | |
32 OnTreeChanging(local_params)); | |
33 } | |
34 } | |
35 | |
36 void NotifyViewTreeChangeUp( | |
37 View* start_at, | |
38 const ViewObserver::TreeChangeParams& params, | |
39 bool change_applied) { | |
40 for (View* current = start_at; current; current = current->parent()) | |
41 NotifyViewTreeChangeAtReceiver(current, params, change_applied); | |
42 } | |
43 | |
44 void NotifyViewTreeChangeDown( | |
45 View* start_at, | |
46 const ViewObserver::TreeChangeParams& params, | |
47 bool change_applied) { | |
48 NotifyViewTreeChangeAtReceiver(start_at, params, change_applied); | |
49 View::Children::const_iterator it = start_at->children().begin(); | |
50 for (; it != start_at->children().end(); ++it) | |
51 NotifyViewTreeChangeDown(*it, params, change_applied); | |
52 } | |
53 | |
54 void NotifyViewTreeChange( | |
55 const ViewObserver::TreeChangeParams& params, | |
56 bool change_applied) { | |
57 NotifyViewTreeChangeDown(params.target, params, change_applied); | |
58 if (params.old_parent) | |
59 NotifyViewTreeChangeUp(params.old_parent, params, change_applied); | |
60 if (params.new_parent) | |
61 NotifyViewTreeChangeUp(params.new_parent, params, change_applied); | |
62 } | |
63 | |
64 class ScopedTreeNotifier { | |
65 public: | |
66 ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) { | |
67 params_.target = target; | |
68 params_.old_parent = old_parent; | |
69 params_.new_parent = new_parent; | |
70 NotifyViewTreeChange(params_, false); | |
71 } | |
72 ~ScopedTreeNotifier() { | |
73 NotifyViewTreeChange(params_, true); | |
74 } | |
75 | |
76 private: | |
77 ViewObserver::TreeChangeParams params_; | |
78 | |
79 DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); | |
80 }; | |
81 | |
82 void RemoveChildImpl(View* child, View::Children* children) { | |
83 View::Children::iterator it = | |
84 std::find(children->begin(), children->end(), child); | |
85 if (it != children->end()) { | |
86 children->erase(it); | |
87 ViewPrivate(child).ClearParent(); | |
88 } | |
89 } | |
90 | |
91 class ScopedOrderChangedNotifier { | |
92 public: | |
93 ScopedOrderChangedNotifier(View* view, | |
94 View* relative_view, | |
95 OrderDirection direction) | |
96 : view_(view), | |
97 relative_view_(relative_view), | |
98 direction_(direction) { | |
99 FOR_EACH_OBSERVER(ViewObserver, | |
100 *ViewPrivate(view_).observers(), | |
101 OnViewReordering(view_, relative_view_, direction_)); | |
102 } | |
103 ~ScopedOrderChangedNotifier() { | |
104 FOR_EACH_OBSERVER(ViewObserver, | |
105 *ViewPrivate(view_).observers(), | |
106 OnViewReordered(view_, relative_view_, direction_)); | |
107 } | |
108 | |
109 private: | |
110 View* view_; | |
111 View* relative_view_; | |
112 OrderDirection direction_; | |
113 | |
114 DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier); | |
115 }; | |
116 | |
117 // Returns true if the order actually changed. | |
118 bool ReorderImpl(View::Children* children, | |
119 View* view, | |
120 View* relative, | |
121 OrderDirection direction) { | |
122 DCHECK(relative); | |
123 DCHECK_NE(view, relative); | |
124 DCHECK_EQ(view->parent(), relative->parent()); | |
125 | |
126 const size_t child_i = | |
127 std::find(children->begin(), children->end(), view) - children->begin(); | |
128 const size_t target_i = | |
129 std::find(children->begin(), children->end(), relative) - | |
130 children->begin(); | |
131 if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) || | |
132 (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) { | |
133 return false; | |
134 } | |
135 | |
136 ScopedOrderChangedNotifier notifier(view, relative, direction); | |
137 | |
138 const size_t dest_i = direction == ORDER_DIRECTION_ABOVE | |
139 ? (child_i < target_i ? target_i : target_i + 1) | |
140 : (child_i < target_i ? target_i - 1 : target_i); | |
141 children->erase(children->begin() + child_i); | |
142 children->insert(children->begin() + dest_i, view); | |
143 | |
144 return true; | |
145 } | |
146 | |
147 class ScopedSetBoundsNotifier { | |
148 public: | |
149 ScopedSetBoundsNotifier(View* view, | |
150 const Rect& old_bounds, | |
151 const Rect& new_bounds) | |
152 : view_(view), | |
153 old_bounds_(old_bounds), | |
154 new_bounds_(new_bounds) { | |
155 FOR_EACH_OBSERVER(ViewObserver, | |
156 *ViewPrivate(view_).observers(), | |
157 OnViewBoundsChanging(view_, old_bounds_, new_bounds_)); | |
158 } | |
159 ~ScopedSetBoundsNotifier() { | |
160 FOR_EACH_OBSERVER(ViewObserver, | |
161 *ViewPrivate(view_).observers(), | |
162 OnViewBoundsChanged(view_, old_bounds_, new_bounds_)); | |
163 } | |
164 | |
165 private: | |
166 View* view_; | |
167 const Rect old_bounds_; | |
168 const Rect new_bounds_; | |
169 | |
170 DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); | |
171 }; | |
172 | |
173 // Some operations are only permitted in the connection that created the view. | |
174 bool OwnsView(ViewManager* manager, View* view) { | |
175 return !manager || | |
176 static_cast<ViewManagerClientImpl*>(manager)->OwnsView(view->id()); | |
177 } | |
178 | |
179 } // namespace | |
180 | |
181 //////////////////////////////////////////////////////////////////////////////// | |
182 // View, public: | |
183 | |
184 // static | |
185 View* View::Create(ViewManager* view_manager) { | |
186 View* view = new View(view_manager); | |
187 static_cast<ViewManagerClientImpl*>(view_manager)->AddView(view); | |
188 return view; | |
189 } | |
190 | |
191 void View::Destroy() { | |
192 if (!OwnsView(manager_, this)) | |
193 return; | |
194 | |
195 if (manager_) | |
196 static_cast<ViewManagerClientImpl*>(manager_)->DestroyView(id_); | |
197 while (!children_.empty()) { | |
198 View* child = children_.front(); | |
199 if (!OwnsView(manager_, child)) { | |
200 ViewPrivate(child).ClearParent(); | |
201 children_.erase(children_.begin()); | |
202 } else { | |
203 child->Destroy(); | |
204 DCHECK(std::find(children_.begin(), children_.end(), child) == | |
205 children_.end()); | |
206 } | |
207 } | |
208 LocalDestroy(); | |
209 } | |
210 | |
211 void View::SetBounds(const Rect& bounds) { | |
212 if (!OwnsView(manager_, this)) | |
213 return; | |
214 | |
215 if (bounds_.Equals(bounds)) | |
216 return; | |
217 | |
218 if (manager_) | |
219 static_cast<ViewManagerClientImpl*>(manager_)->SetBounds(id_, bounds); | |
220 LocalSetBounds(bounds_, bounds); | |
221 } | |
222 | |
223 void View::SetVisible(bool value) { | |
224 if (visible_ == value) | |
225 return; | |
226 | |
227 if (manager_) | |
228 static_cast<ViewManagerClientImpl*>(manager_)->SetVisible(id_, value); | |
229 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this)); | |
230 visible_ = value; | |
231 NotifyViewVisibilityChanged(this); | |
232 } | |
233 | |
234 void View::SetSharedProperty(const std::string& name, | |
235 const std::vector<uint8_t>* value) { | |
236 std::vector<uint8_t> old_value; | |
237 std::vector<uint8_t>* old_value_ptr = nullptr; | |
238 auto it = properties_.find(name); | |
239 if (it != properties_.end()) { | |
240 old_value = it->second; | |
241 old_value_ptr = &old_value; | |
242 | |
243 if (value && old_value == *value) | |
244 return; | |
245 } else if (!value) { | |
246 // This property isn't set in |properties_| and |value| is NULL, so there's | |
247 // no change. | |
248 return; | |
249 } | |
250 | |
251 if (value) { | |
252 properties_[name] = *value; | |
253 } else if (it != properties_.end()) { | |
254 properties_.erase(it); | |
255 } | |
256 | |
257 FOR_EACH_OBSERVER( | |
258 ViewObserver, observers_, | |
259 OnViewSharedPropertyChanged(this, name, old_value_ptr, value)); | |
260 } | |
261 | |
262 bool View::IsDrawn() const { | |
263 if (!visible_) | |
264 return false; | |
265 return parent_ ? parent_->IsDrawn() : drawn_; | |
266 } | |
267 | |
268 void View::AddObserver(ViewObserver* observer) { | |
269 observers_.AddObserver(observer); | |
270 } | |
271 | |
272 void View::RemoveObserver(ViewObserver* observer) { | |
273 observers_.RemoveObserver(observer); | |
274 } | |
275 | |
276 const View* View::GetRoot() const { | |
277 const View* root = this; | |
278 for (const View* parent = this; parent; parent = parent->parent()) | |
279 root = parent; | |
280 return root; | |
281 } | |
282 | |
283 void View::AddChild(View* child) { | |
284 // TODO(beng): not necessarily valid to all connections, but possibly to the | |
285 // embeddee in an embedder-embeddee relationship. | |
286 if (manager_) | |
287 CHECK_EQ(ViewPrivate(child).view_manager(), manager_); | |
288 LocalAddChild(child); | |
289 if (manager_) | |
290 static_cast<ViewManagerClientImpl*>(manager_)->AddChild(child->id(), id_); | |
291 } | |
292 | |
293 void View::RemoveChild(View* child) { | |
294 // TODO(beng): not necessarily valid to all connections, but possibly to the | |
295 // embeddee in an embedder-embeddee relationship. | |
296 if (manager_) | |
297 CHECK_EQ(ViewPrivate(child).view_manager(), manager_); | |
298 LocalRemoveChild(child); | |
299 if (manager_) { | |
300 static_cast<ViewManagerClientImpl*>(manager_)->RemoveChild(child->id(), | |
301 id_); | |
302 } | |
303 } | |
304 | |
305 void View::MoveToFront() { | |
306 if (!parent_ || parent_->children_.back() == this) | |
307 return; | |
308 Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE); | |
309 } | |
310 | |
311 void View::MoveToBack() { | |
312 if (!parent_ || parent_->children_.front() == this) | |
313 return; | |
314 Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW); | |
315 } | |
316 | |
317 void View::Reorder(View* relative, OrderDirection direction) { | |
318 if (!LocalReorder(relative, direction)) | |
319 return; | |
320 if (manager_) { | |
321 static_cast<ViewManagerClientImpl*>(manager_)->Reorder(id_, | |
322 relative->id(), | |
323 direction); | |
324 } | |
325 } | |
326 | |
327 bool View::Contains(View* child) const { | |
328 if (!child) | |
329 return false; | |
330 if (child == this) | |
331 return true; | |
332 if (manager_) | |
333 CHECK_EQ(ViewPrivate(child).view_manager(), manager_); | |
334 for (View* p = child->parent(); p; p = p->parent()) { | |
335 if (p == this) | |
336 return true; | |
337 } | |
338 return false; | |
339 } | |
340 | |
341 View* View::GetChildById(Id id) { | |
342 if (id == id_) | |
343 return this; | |
344 // TODO(beng): this could be improved depending on how we decide to own views. | |
345 Children::const_iterator it = children_.begin(); | |
346 for (; it != children_.end(); ++it) { | |
347 View* view = (*it)->GetChildById(id); | |
348 if (view) | |
349 return view; | |
350 } | |
351 return NULL; | |
352 } | |
353 | |
354 void View::SetSurfaceId(SurfaceIdPtr id) { | |
355 if (manager_) { | |
356 static_cast<ViewManagerClientImpl*>(manager_)->SetSurfaceId(id_, id.Pass()); | |
357 } | |
358 } | |
359 | |
360 void View::SetFocus() { | |
361 if (manager_) | |
362 static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_); | |
363 } | |
364 | |
365 void View::Embed(const String& url) { | |
366 static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_); | |
367 } | |
368 | |
369 scoped_ptr<ServiceProvider> | |
370 View::Embed(const String& url, | |
371 scoped_ptr<ServiceProviderImpl> exported_services) { | |
372 scoped_ptr<ServiceProvider> imported_services; | |
373 // BindToProxy() takes ownership of |exported_services|. | |
374 ServiceProviderImpl* registry = exported_services.release(); | |
375 ServiceProviderPtr sp; | |
376 if (registry) { | |
377 BindToProxy(registry, &sp); | |
378 imported_services.reset(registry->CreateRemoteServiceProvider()); | |
379 } | |
380 static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_, sp.Pass()); | |
381 return imported_services.Pass(); | |
382 } | |
383 | |
384 //////////////////////////////////////////////////////////////////////////////// | |
385 // View, protected: | |
386 | |
387 View::View() | |
388 : manager_(NULL), | |
389 id_(static_cast<Id>(-1)), | |
390 parent_(NULL), | |
391 visible_(true), | |
392 drawn_(false) { | |
393 } | |
394 | |
395 View::~View() { | |
396 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this)); | |
397 if (parent_) | |
398 parent_->LocalRemoveChild(this); | |
399 | |
400 // We may still have children. This can happen if the embedder destroys the | |
401 // root while we're still alive. | |
402 while (!children_.empty()) { | |
403 View* child = children_.front(); | |
404 LocalRemoveChild(child); | |
405 DCHECK(children_.empty() || children_.front() != child); | |
406 } | |
407 | |
408 // TODO(beng): It'd be better to do this via a destruction observer in the | |
409 // ViewManagerClientImpl. | |
410 if (manager_) | |
411 static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_); | |
412 | |
413 // Clear properties. | |
414 for (auto& pair : prop_map_) { | |
415 if (pair.second.deallocator) | |
416 (*pair.second.deallocator)(pair.second.value); | |
417 } | |
418 prop_map_.clear(); | |
419 | |
420 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this)); | |
421 } | |
422 | |
423 //////////////////////////////////////////////////////////////////////////////// | |
424 // View, private: | |
425 | |
426 View::View(ViewManager* manager) | |
427 : manager_(manager), | |
428 id_(static_cast<ViewManagerClientImpl*>(manager_)->CreateView()), | |
429 parent_(NULL), | |
430 visible_(false), | |
431 drawn_(false) { | |
432 } | |
433 | |
434 int64 View::SetLocalPropertyInternal(const void* key, | |
435 const char* name, | |
436 PropertyDeallocator deallocator, | |
437 int64 value, | |
438 int64 default_value) { | |
439 int64 old = GetLocalPropertyInternal(key, default_value); | |
440 if (value == default_value) { | |
441 prop_map_.erase(key); | |
442 } else { | |
443 Value prop_value; | |
444 prop_value.name = name; | |
445 prop_value.value = value; | |
446 prop_value.deallocator = deallocator; | |
447 prop_map_[key] = prop_value; | |
448 } | |
449 FOR_EACH_OBSERVER(ViewObserver, observers_, | |
450 OnViewLocalPropertyChanged(this, key, old)); | |
451 return old; | |
452 } | |
453 | |
454 int64 View::GetLocalPropertyInternal(const void* key, | |
455 int64 default_value) const { | |
456 std::map<const void*, Value>::const_iterator iter = prop_map_.find(key); | |
457 if (iter == prop_map_.end()) | |
458 return default_value; | |
459 return iter->second.value; | |
460 } | |
461 | |
462 void View::LocalDestroy() { | |
463 delete this; | |
464 } | |
465 | |
466 void View::LocalAddChild(View* child) { | |
467 ScopedTreeNotifier notifier(child, child->parent(), this); | |
468 if (child->parent()) | |
469 RemoveChildImpl(child, &child->parent_->children_); | |
470 children_.push_back(child); | |
471 child->parent_ = this; | |
472 } | |
473 | |
474 void View::LocalRemoveChild(View* child) { | |
475 DCHECK_EQ(this, child->parent()); | |
476 ScopedTreeNotifier notifier(child, this, NULL); | |
477 RemoveChildImpl(child, &children_); | |
478 } | |
479 | |
480 bool View::LocalReorder(View* relative, OrderDirection direction) { | |
481 return ReorderImpl(&parent_->children_, this, relative, direction); | |
482 } | |
483 | |
484 void View::LocalSetBounds(const Rect& old_bounds, | |
485 const Rect& new_bounds) { | |
486 DCHECK(old_bounds.x == bounds_.x); | |
487 DCHECK(old_bounds.y == bounds_.y); | |
488 DCHECK(old_bounds.width == bounds_.width); | |
489 DCHECK(old_bounds.height == bounds_.height); | |
490 ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds); | |
491 bounds_ = new_bounds; | |
492 } | |
493 | |
494 void View::LocalSetDrawn(bool value) { | |
495 if (drawn_ == value) | |
496 return; | |
497 | |
498 // As IsDrawn() is derived from |visible_| and |drawn_|, only send drawn | |
499 // notification is the value of IsDrawn() is really changing. | |
500 if (IsDrawn() == value) { | |
501 drawn_ = value; | |
502 return; | |
503 } | |
504 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanging(this)); | |
505 drawn_ = value; | |
506 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanged(this)); | |
507 } | |
508 | |
509 void View::NotifyViewVisibilityChanged(View* target) { | |
510 if (!NotifyViewVisibilityChangedDown(target)) { | |
511 return; // |this| has been deleted. | |
512 } | |
513 NotifyViewVisibilityChangedUp(target); | |
514 } | |
515 | |
516 bool View::NotifyViewVisibilityChangedAtReceiver(View* target) { | |
517 // |this| may be deleted during a call to OnViewVisibilityChanged() on one | |
518 // of the observers. We create an local observer for that. In that case we | |
519 // exit without further access to any members. | |
520 ViewTracker tracker; | |
521 tracker.Add(this); | |
522 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(target)); | |
523 return tracker.Contains(this); | |
524 } | |
525 | |
526 bool View::NotifyViewVisibilityChangedDown(View* target) { | |
527 if (!NotifyViewVisibilityChangedAtReceiver(target)) | |
528 return false; // |this| was deleted. | |
529 std::set<const View*> child_already_processed; | |
530 bool child_destroyed = false; | |
531 do { | |
532 child_destroyed = false; | |
533 for (View::Children::const_iterator it = children_.begin(); | |
534 it != children_.end(); ++it) { | |
535 if (!child_already_processed.insert(*it).second) | |
536 continue; | |
537 if (!(*it)->NotifyViewVisibilityChangedDown(target)) { | |
538 // |*it| was deleted, |it| is invalid and |children_| has changed. We | |
539 // exit the current for-loop and enter a new one. | |
540 child_destroyed = true; | |
541 break; | |
542 } | |
543 } | |
544 } while (child_destroyed); | |
545 return true; | |
546 } | |
547 | |
548 void View::NotifyViewVisibilityChangedUp(View* target) { | |
549 // Start with the parent as we already notified |this| | |
550 // in NotifyViewVisibilityChangedDown. | |
551 for (View* view = parent(); view; view = view->parent()) { | |
552 bool ret = view->NotifyViewVisibilityChangedAtReceiver(target); | |
553 DCHECK(ret); | |
554 } | |
555 } | |
556 | |
557 } // namespace mojo | |
OLD | NEW |