| 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 |