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