Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Side by Side Diff: components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc

Issue 2018823002: Eliminate WindowTreeConnection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@connection
Patch Set: . Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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/mus/public/cpp/lib/window_tree_client_impl.h"
6
7 #include <stdint.h>
8
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "components/mus/common/util.h"
12 #include "components/mus/public/cpp/input_event_handler.h"
13 #include "components/mus/public/cpp/lib/window_private.h"
14 #include "components/mus/public/cpp/property_type_converters.h"
15 #include "components/mus/public/cpp/tests/test_window.h"
16 #include "components/mus/public/cpp/tests/test_window_tree.h"
17 #include "components/mus/public/cpp/tests/window_tree_client_impl_private.h"
18 #include "components/mus/public/cpp/window.h"
19 #include "components/mus/public/cpp/window_observer.h"
20 #include "components/mus/public/cpp/window_property.h"
21 #include "components/mus/public/cpp/window_tracker.h"
22 #include "components/mus/public/cpp/window_tree_delegate.h"
23 #include "mojo/common/common_type_converters.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "ui/events/event.h"
26 #include "ui/events/event_utils.h"
27 #include "ui/events/mojo/input_events_type_converters.h"
28 #include "ui/gfx/geometry/mojo/geometry_type_converters.h"
29 #include "ui/gfx/geometry/rect.h"
30
31 namespace mus {
32
33 namespace {
34
35 void DoNothingWithEventResult(mojom::EventResult result) {}
36
37 Id server_id(mus::Window* window) {
38 return WindowPrivate(window).server_id();
39 }
40
41 } // namespace
42
43 mojo::Array<uint8_t> Int32ToPropertyTransportValue(int32_t value) {
44 const std::vector<uint8_t> bytes =
45 mojo::ConvertTo<std::vector<uint8_t>>(value);
46 mojo::Array<uint8_t> transport_value;
47 transport_value.resize(bytes.size());
48 memcpy(&transport_value.front(), &(bytes.front()), bytes.size());
49 return transport_value;
50 }
51
52 class TestWindowTreeDelegate : public WindowTreeDelegate {
53 public:
54 TestWindowTreeDelegate() {}
55 ~TestWindowTreeDelegate() override {}
56
57 ui::Event* last_event_observed() { return last_event_observed_.get(); }
58
59 void Reset() { last_event_observed_.reset(); }
60
61 // WindowTreeDelegate:
62 void OnEmbed(Window* root) override {}
63 void OnConnectionLost(WindowTreeConnection* connection) override {}
64 void OnEventObserved(const ui::Event& event, Window* target) override {
65 last_event_observed_ = ui::Event::Clone(event);
66 }
67
68 private:
69 std::unique_ptr<ui::Event> last_event_observed_;
70
71 DISALLOW_COPY_AND_ASSIGN(TestWindowTreeDelegate);
72 };
73
74 class WindowTreeSetup {
75 public:
76 WindowTreeSetup() : tree_client_(&window_tree_delegate_, nullptr, nullptr) {
77 WindowTreeClientImplPrivate(&tree_client_).OnEmbed(&window_tree_);
78 window_tree_.GetAndClearChangeId(nullptr);
79 }
80
81 WindowTreeConnection* window_tree_connection() {
82 return static_cast<WindowTreeConnection*>(&tree_client_);
83 }
84
85 mojom::WindowTreeClient* window_tree_client() {
86 return static_cast<mojom::WindowTreeClient*>(&tree_client_);
87 }
88
89 TestWindowTree* window_tree() { return &window_tree_; }
90
91 TestWindowTreeDelegate* window_tree_delegate() {
92 return &window_tree_delegate_;
93 }
94
95 Window* GetFirstRoot() {
96 return window_tree_connection()->GetRoots().empty()
97 ? nullptr
98 : *window_tree_connection()->GetRoots().begin();
99 }
100
101 uint32_t GetEventObserverId() {
102 return WindowTreeClientImplPrivate(&tree_client_).event_observer_id();
103 }
104
105 private:
106 TestWindowTree window_tree_;
107 TestWindowTreeDelegate window_tree_delegate_;
108 WindowTreeClientImpl tree_client_;
109
110 DISALLOW_COPY_AND_ASSIGN(WindowTreeSetup);
111 };
112
113 class TestInputEventHandler : public InputEventHandler {
114 public:
115 TestInputEventHandler()
116 : received_event_(false), should_manually_ack_(false) {}
117 ~TestInputEventHandler() override {}
118
119 void set_should_manually_ack() { should_manually_ack_ = true; }
120
121 void AckEvent() {
122 DCHECK(should_manually_ack_);
123 DCHECK(!ack_callback_.is_null());
124 ack_callback_.Run(mojom::EventResult::HANDLED);
125 ack_callback_ = base::Bind(&DoNothingWithEventResult);
126 }
127
128 void Reset() {
129 received_event_ = false;
130 ack_callback_ = base::Bind(&DoNothingWithEventResult);
131 }
132 bool received_event() const { return received_event_; }
133
134 private:
135 // InputEventHandler:
136 void OnWindowInputEvent(
137 Window* target,
138 const ui::Event& event,
139 std::unique_ptr<base::Callback<void(mojom::EventResult)>>* ack_callback)
140 override {
141 EXPECT_FALSE(received_event_)
142 << "Observer was not reset after receiving event.";
143 received_event_ = true;
144 if (should_manually_ack_) {
145 ack_callback_ = *ack_callback->get();
146 ack_callback->reset();
147 }
148 }
149
150 bool received_event_;
151 bool should_manually_ack_;
152 base::Callback<void(mojom::EventResult)> ack_callback_;
153
154 DISALLOW_COPY_AND_ASSIGN(TestInputEventHandler);
155 };
156
157 using WindowTreeClientImplTest = testing::Test;
158
159 // Verifies bounds are reverted if the server replied that the change failed.
160 TEST_F(WindowTreeClientImplTest, SetBoundsFailed) {
161 WindowTreeSetup setup;
162 Window* root = setup.GetFirstRoot();
163 ASSERT_TRUE(root);
164 const gfx::Rect original_bounds(root->bounds());
165 const gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100));
166 ASSERT_NE(new_bounds, root->bounds());
167 root->SetBounds(new_bounds);
168 uint32_t change_id;
169 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
170 setup.window_tree_client()->OnChangeCompleted(change_id, false);
171 EXPECT_EQ(original_bounds, root->bounds());
172 }
173
174 // Simulates a bounds change, and while the bounds change is in flight the
175 // server replies with a new bounds and the original bounds change fails.
176 TEST_F(WindowTreeClientImplTest, SetBoundsFailedWithPendingChange) {
177 WindowTreeSetup setup;
178 Window* root = setup.GetFirstRoot();
179 ASSERT_TRUE(root);
180 const gfx::Rect original_bounds(root->bounds());
181 const gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100));
182 ASSERT_NE(new_bounds, root->bounds());
183 root->SetBounds(new_bounds);
184 EXPECT_EQ(new_bounds, root->bounds());
185 uint32_t change_id;
186 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
187
188 // Simulate the server responding with a bounds change.
189 const gfx::Rect server_changed_bounds(gfx::Rect(0, 0, 101, 102));
190 setup.window_tree_client()->OnWindowBoundsChanged(
191 server_id(root), mojo::Rect::From(original_bounds),
192 mojo::Rect::From(server_changed_bounds));
193
194 // This shouldn't trigger the bounds changing yet.
195 EXPECT_EQ(new_bounds, root->bounds());
196
197 // Tell the client the change failed, which should trigger failing to the
198 // most recent bounds from server.
199 setup.window_tree_client()->OnChangeCompleted(change_id, false);
200 EXPECT_EQ(server_changed_bounds, root->bounds());
201
202 // Simulate server changing back to original bounds. Should take immediately.
203 setup.window_tree_client()->OnWindowBoundsChanged(
204 server_id(root), mojo::Rect::From(server_changed_bounds),
205 mojo::Rect::From(original_bounds));
206 EXPECT_EQ(original_bounds, root->bounds());
207 }
208
209 TEST_F(WindowTreeClientImplTest, TwoInFlightBoundsChangesBothCanceled) {
210 WindowTreeSetup setup;
211 Window* root = setup.GetFirstRoot();
212 ASSERT_TRUE(root);
213 const gfx::Rect original_bounds(root->bounds());
214 const gfx::Rect bounds1(gfx::Rect(0, 0, 100, 100));
215 const gfx::Rect bounds2(gfx::Rect(0, 0, 100, 102));
216 root->SetBounds(bounds1);
217 EXPECT_EQ(bounds1, root->bounds());
218 uint32_t change_id1;
219 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1));
220
221 root->SetBounds(bounds2);
222 EXPECT_EQ(bounds2, root->bounds());
223 uint32_t change_id2;
224 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2));
225
226 // Tell the client change 1 failed. As there is a still a change in flight
227 // nothing should happen.
228 setup.window_tree_client()->OnChangeCompleted(change_id1, false);
229 EXPECT_EQ(bounds2, root->bounds());
230
231 // And tell the client change 2 failed too. Should now fallback to original
232 // bounds.
233 setup.window_tree_client()->OnChangeCompleted(change_id2, false);
234 EXPECT_EQ(original_bounds, root->bounds());
235 }
236
237 // Verifies properties are reverted if the server replied that the change
238 // failed.
239 TEST_F(WindowTreeClientImplTest, SetPropertyFailed) {
240 WindowTreeSetup setup;
241 Window* root = setup.GetFirstRoot();
242 ASSERT_TRUE(root);
243 ASSERT_FALSE(root->HasSharedProperty("foo"));
244 const int32_t new_value = 11;
245 root->SetSharedProperty("foo", new_value);
246 ASSERT_TRUE(root->HasSharedProperty("foo"));
247 EXPECT_EQ(new_value, root->GetSharedProperty<int32_t>("foo"));
248 uint32_t change_id;
249 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
250 setup.window_tree_client()->OnChangeCompleted(change_id, false);
251 EXPECT_FALSE(root->HasSharedProperty("foo"));
252 }
253
254 // Simulates a property change, and while the property change is in flight the
255 // server replies with a new property and the original property change fails.
256 TEST_F(WindowTreeClientImplTest, SetPropertyFailedWithPendingChange) {
257 WindowTreeSetup setup;
258 Window* root = setup.GetFirstRoot();
259 ASSERT_TRUE(root);
260 const int32_t value1 = 11;
261 root->SetSharedProperty("foo", value1);
262 ASSERT_TRUE(root->HasSharedProperty("foo"));
263 EXPECT_EQ(value1, root->GetSharedProperty<int32_t>("foo"));
264 uint32_t change_id;
265 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
266
267 // Simulate the server responding with a different value.
268 const int32_t server_value = 12;
269 setup.window_tree_client()->OnWindowSharedPropertyChanged(
270 server_id(root), "foo", Int32ToPropertyTransportValue(server_value));
271
272 // This shouldn't trigger the property changing yet.
273 ASSERT_TRUE(root->HasSharedProperty("foo"));
274 EXPECT_EQ(value1, root->GetSharedProperty<int32_t>("foo"));
275
276 // Tell the client the change failed, which should trigger failing to the
277 // most recent value from server.
278 setup.window_tree_client()->OnChangeCompleted(change_id, false);
279 ASSERT_TRUE(root->HasSharedProperty("foo"));
280 EXPECT_EQ(server_value, root->GetSharedProperty<int32_t>("foo"));
281
282 // Simulate server changing back to value1. Should take immediately.
283 setup.window_tree_client()->OnWindowSharedPropertyChanged(
284 server_id(root), "foo", Int32ToPropertyTransportValue(value1));
285 ASSERT_TRUE(root->HasSharedProperty("foo"));
286 EXPECT_EQ(value1, root->GetSharedProperty<int32_t>("foo"));
287 }
288
289 // Verifies visible is reverted if the server replied that the change failed.
290 TEST_F(WindowTreeClientImplTest, SetVisibleFailed) {
291 WindowTreeSetup setup;
292 Window* root = setup.GetFirstRoot();
293 ASSERT_TRUE(root);
294 const bool original_visible = root->visible();
295 const bool new_visible = !original_visible;
296 ASSERT_NE(new_visible, root->visible());
297 root->SetVisible(new_visible);
298 uint32_t change_id;
299 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
300 setup.window_tree_client()->OnChangeCompleted(change_id, false);
301 EXPECT_EQ(original_visible, root->visible());
302 }
303
304 // Simulates a visible change, and while the visible change is in flight the
305 // server replies with a new visible and the original visible change fails.
306 TEST_F(WindowTreeClientImplTest, SetVisibleFailedWithPendingChange) {
307 WindowTreeSetup setup;
308 Window* root = setup.GetFirstRoot();
309 ASSERT_TRUE(root);
310 const bool original_visible = root->visible();
311 const bool new_visible = !original_visible;
312 ASSERT_NE(new_visible, root->visible());
313 root->SetVisible(new_visible);
314 EXPECT_EQ(new_visible, root->visible());
315 uint32_t change_id;
316 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
317
318 // Simulate the server responding with a visible change.
319 const bool server_changed_visible = !new_visible;
320 setup.window_tree_client()->OnWindowVisibilityChanged(server_id(root),
321 server_changed_visible);
322
323 // This shouldn't trigger visible changing yet.
324 EXPECT_EQ(new_visible, root->visible());
325
326 // Tell the client the change failed, which should trigger failing to the
327 // most recent visible from server.
328 setup.window_tree_client()->OnChangeCompleted(change_id, false);
329 EXPECT_EQ(server_changed_visible, root->visible());
330
331 // Simulate server changing back to original visible. Should take immediately.
332 setup.window_tree_client()->OnWindowVisibilityChanged(server_id(root),
333 original_visible);
334 EXPECT_EQ(original_visible, root->visible());
335 }
336
337 // Verifies that local opacity is not changed if the server replied that the
338 // change succeeded.
339 TEST_F(WindowTreeClientImplTest, SetOpacitySucceeds) {
340 WindowTreeSetup setup;
341 Window* root = setup.GetFirstRoot();
342 ASSERT_TRUE(root);
343 const float original_opacity = root->opacity();
344 const float new_opacity = 0.5f;
345 ASSERT_NE(new_opacity, original_opacity);
346 ASSERT_NE(new_opacity, root->opacity());
347 root->SetOpacity(new_opacity);
348 uint32_t change_id;
349 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
350 setup.window_tree_client()->OnChangeCompleted(change_id, true);
351 EXPECT_EQ(new_opacity, root->opacity());
352 }
353
354 // Verifies that opacity is reverted if the server replied that the change
355 // failed.
356 TEST_F(WindowTreeClientImplTest, SetOpacityFailed) {
357 WindowTreeSetup setup;
358 Window* root = setup.GetFirstRoot();
359 ASSERT_TRUE(root);
360 const float original_opacity = root->opacity();
361 const float new_opacity = 0.5f;
362 ASSERT_NE(new_opacity, root->opacity());
363 root->SetOpacity(new_opacity);
364 uint32_t change_id;
365 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
366 setup.window_tree_client()->OnChangeCompleted(change_id, false);
367 EXPECT_EQ(original_opacity, root->opacity());
368 }
369
370 // Simulates the server changing the opacitry while there is an opacity change
371 // in flight, causing the requested change to fail.
372 TEST_F(WindowTreeClientImplTest, SetOpacityFailedWithPendingChange) {
373 WindowTreeSetup setup;
374 Window* root = setup.GetFirstRoot();
375 ASSERT_TRUE(root);
376 const float original_opacity = root->opacity();
377 const float new_opacity = 0.5f;
378 ASSERT_NE(new_opacity, root->opacity());
379 root->SetOpacity(new_opacity);
380 EXPECT_EQ(new_opacity, root->opacity());
381 uint32_t change_id;
382 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
383
384 // Simulate the server responding with an opacity change.
385 const float server_changed_opacity = 0.75f;
386 setup.window_tree_client()->OnWindowOpacityChanged(
387 server_id(root), original_opacity, server_changed_opacity);
388
389 // This shouldn't trigger opacity changing yet.
390 EXPECT_EQ(new_opacity, root->opacity());
391
392 // Tell the client the change failed, which should trigger failing to the
393 // most recent opacity from server.
394 setup.window_tree_client()->OnChangeCompleted(change_id, false);
395 EXPECT_EQ(server_changed_opacity, root->opacity());
396
397 // Simulate server changing back to original opacity. Should take immediately.
398 setup.window_tree_client()->OnWindowOpacityChanged(
399 server_id(root), server_changed_opacity, original_opacity);
400 EXPECT_EQ(original_opacity, root->opacity());
401 }
402
403 // Tests that when there are multiple changes in flight, that failing changes
404 // update the revert state of subsequent changes.
405 TEST_F(WindowTreeClientImplTest, SetOpacityFailedWithMultiplePendingChange) {
406 WindowTreeSetup setup;
407 Window* root = setup.GetFirstRoot();
408 ASSERT_TRUE(root);
409 const float original_opacity = root->opacity();
410 const float new_opacity = 0.5f;
411 ASSERT_NE(new_opacity, root->opacity());
412 root->SetOpacity(new_opacity);
413 uint32_t change_id1;
414 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1));
415
416 const float second_new_opacity = 0.75f;
417 ASSERT_NE(second_new_opacity, root->opacity());
418 root->SetOpacity(second_new_opacity);
419 uint32_t change_id2;
420 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2));
421
422 // Canceling the first one, while there is another in flight, should not
423 // change the local opacity.
424 setup.window_tree_client()->OnChangeCompleted(change_id1, false);
425 EXPECT_EQ(second_new_opacity, root->opacity());
426
427 // The previous cancelation should have updated the revert value of the in
428 // flight change.
429 setup.window_tree_client()->OnChangeCompleted(change_id2, false);
430 EXPECT_EQ(original_opacity, root->opacity());
431 }
432
433 // Verifies |is_modal| is reverted if the server replied that the change failed.
434 TEST_F(WindowTreeClientImplTest, SetModalFailed) {
435 WindowTreeSetup setup;
436 Window* root = setup.GetFirstRoot();
437 ASSERT_TRUE(root);
438 EXPECT_FALSE(root->is_modal());
439 root->SetModal();
440 uint32_t change_id;
441 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
442 EXPECT_TRUE(root->is_modal());
443 setup.window_tree_client()->OnChangeCompleted(change_id, false);
444 EXPECT_FALSE(root->is_modal());
445 }
446
447 TEST_F(WindowTreeClientImplTest, InputEventBasic) {
448 WindowTreeSetup setup;
449 Window* root = setup.GetFirstRoot();
450 ASSERT_TRUE(root);
451
452 TestInputEventHandler event_handler;
453 root->set_input_event_handler(&event_handler);
454
455 std::unique_ptr<ui::Event> ui_event(
456 new ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
457 ui::EventTimeForNow(), ui::EF_NONE, 0));
458 setup.window_tree_client()->OnWindowInputEvent(
459 1, server_id(root), mojom::Event::From(*ui_event.get()), 0);
460 EXPECT_TRUE(event_handler.received_event());
461 EXPECT_TRUE(setup.window_tree()->WasEventAcked(1));
462 event_handler.Reset();
463
464 event_handler.set_should_manually_ack();
465 setup.window_tree_client()->OnWindowInputEvent(
466 33, server_id(root), mojom::Event::From(*ui_event.get()), 0);
467 EXPECT_TRUE(event_handler.received_event());
468 EXPECT_FALSE(setup.window_tree()->WasEventAcked(33));
469
470 event_handler.AckEvent();
471 EXPECT_TRUE(setup.window_tree()->WasEventAcked(33));
472 }
473
474 // Tests event observers triggered by events that did not hit a target in this
475 // window tree.
476 TEST_F(WindowTreeClientImplTest, OnEventObserved) {
477 WindowTreeSetup setup;
478 Window* root = setup.GetFirstRoot();
479 ASSERT_TRUE(root);
480
481 // Set up an event observer.
482 mojom::EventMatcherPtr matcher = mojom::EventMatcher::New();
483 matcher->type_matcher = mojom::EventTypeMatcher::New();
484 matcher->type_matcher->type = mojom::EventType::POINTER_DOWN;
485 setup.window_tree_connection()->SetEventObserver(std::move(matcher));
486
487 // Simulate the server sending an observed event.
488 uint32_t event_observer_id = setup.GetEventObserverId();
489 std::unique_ptr<ui::Event> ui_event(
490 new ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
491 ui::EventTimeForNow(), ui::EF_CONTROL_DOWN, 0));
492 setup.window_tree_client()->OnEventObserved(
493 mojom::Event::From(*ui_event.get()), event_observer_id);
494
495 // Delegate sensed the event.
496 ui::Event* last_event = setup.window_tree_delegate()->last_event_observed();
497 EXPECT_EQ(ui::ET_MOUSE_PRESSED, last_event->type());
498 EXPECT_EQ(ui::EF_CONTROL_DOWN, last_event->flags());
499 setup.window_tree_delegate()->Reset();
500
501 // Clear the event observer.
502 setup.window_tree_connection()->SetEventObserver(nullptr);
503
504 // Simulate another event from the server.
505 setup.window_tree_client()->OnEventObserved(
506 mojom::Event::From(*ui_event.get()), event_observer_id);
507
508 // No event was sensed.
509 EXPECT_FALSE(setup.window_tree_delegate()->last_event_observed());
510 }
511
512 // Tests event observers triggered by events that hit this window tree.
513 TEST_F(WindowTreeClientImplTest, OnWindowInputEventWithEventObserver) {
514 WindowTreeSetup setup;
515 Window* root = setup.GetFirstRoot();
516 ASSERT_TRUE(root);
517
518 // Set up an event observer.
519 mojom::EventMatcherPtr matcher = mojom::EventMatcher::New();
520 matcher->type_matcher = mojom::EventTypeMatcher::New();
521 matcher->type_matcher->type = mojom::EventType::POINTER_DOWN;
522 setup.window_tree_connection()->SetEventObserver(std::move(matcher));
523
524 // Simulate the server dispatching an event that also matched the observer.
525 uint32_t event_observer_id = setup.GetEventObserverId();
526 std::unique_ptr<ui::Event> ui_event(
527 new ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
528 ui::EventTimeForNow(), ui::EF_CONTROL_DOWN, 0));
529 setup.window_tree_client()->OnWindowInputEvent(
530 1, server_id(root), mojom::Event::From(*ui_event.get()),
531 event_observer_id);
532
533 // Delegate sensed the event.
534 ui::Event* last_event = setup.window_tree_delegate()->last_event_observed();
535 EXPECT_EQ(ui::ET_MOUSE_PRESSED, last_event->type());
536 EXPECT_EQ(ui::EF_CONTROL_DOWN, last_event->flags());
537 }
538
539 // Tests that replacing an event observer with a new one results in only new
540 // events being observed.
541 TEST_F(WindowTreeClientImplTest, EventObserverReplaced) {
542 WindowTreeSetup setup;
543 Window* root = setup.GetFirstRoot();
544 ASSERT_TRUE(root);
545
546 // Set up an event observer.
547 mojom::EventMatcherPtr matcher1 = mojom::EventMatcher::New();
548 matcher1->type_matcher = mojom::EventTypeMatcher::New();
549 matcher1->type_matcher->type = mojom::EventType::POINTER_DOWN;
550 setup.window_tree_connection()->SetEventObserver(std::move(matcher1));
551 uint32_t event_observer_id1 = setup.GetEventObserverId();
552
553 // Replace it with a second observer.
554 mojom::EventMatcherPtr matcher2 = mojom::EventMatcher::New();
555 matcher2->type_matcher = mojom::EventTypeMatcher::New();
556 matcher2->type_matcher->type = mojom::EventType::POINTER_UP;
557 setup.window_tree_connection()->SetEventObserver(std::move(matcher2));
558 uint32_t event_observer_id2 = setup.GetEventObserverId();
559
560 // Simulate the server sending an observed event that matched the old observer
561 // (e.g. that was in-flight when the observer was replaced).
562 std::unique_ptr<ui::Event> pressed_event(
563 new ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
564 ui::EventTimeForNow(), ui::EF_NONE, 0));
565 setup.window_tree_client()->OnEventObserved(
566 mojom::Event::From(*pressed_event.get()), event_observer_id1);
567
568 // The event was not sensed, because it does not match the current observer.
569 EXPECT_FALSE(setup.window_tree_delegate()->last_event_observed());
570
571 // Simulate another event that matches the new observer.
572 std::unique_ptr<ui::Event> released_event(
573 new ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
574 ui::EventTimeForNow(), ui::EF_CONTROL_DOWN, 0));
575 setup.window_tree_client()->OnEventObserved(
576 mojom::Event::From(*released_event.get()), event_observer_id2);
577
578 // The delegate sensed the event.
579 ui::Event* last_event = setup.window_tree_delegate()->last_event_observed();
580 EXPECT_EQ(ui::ET_MOUSE_RELEASED, last_event->type());
581 EXPECT_EQ(ui::EF_CONTROL_DOWN, last_event->flags());
582 }
583
584 // Verifies focus is reverted if the server replied that the change failed.
585 TEST_F(WindowTreeClientImplTest, SetFocusFailed) {
586 WindowTreeSetup setup;
587 Window* root = setup.GetFirstRoot();
588 ASSERT_TRUE(root);
589 root->SetVisible(true);
590 Window* child = setup.window_tree_connection()->NewWindow();
591 child->SetVisible(true);
592 root->AddChild(child);
593 Window* original_focus = setup.window_tree_connection()->GetFocusedWindow();
594 Window* new_focus = child;
595 ASSERT_NE(new_focus, original_focus);
596 new_focus->SetFocus();
597 ASSERT_TRUE(new_focus->HasFocus());
598 uint32_t change_id;
599 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
600 setup.window_tree_client()->OnChangeCompleted(change_id, false);
601 EXPECT_EQ(original_focus, setup.window_tree_connection()->GetFocusedWindow());
602 }
603
604 // Simulates a focus change, and while the focus change is in flight the server
605 // replies with a new focus and the original focus change fails.
606 TEST_F(WindowTreeClientImplTest, SetFocusFailedWithPendingChange) {
607 WindowTreeSetup setup;
608 Window* root = setup.GetFirstRoot();
609 ASSERT_TRUE(root);
610 root->SetVisible(true);
611 Window* child1 = setup.window_tree_connection()->NewWindow();
612 child1->SetVisible(true);
613 root->AddChild(child1);
614 Window* child2 = setup.window_tree_connection()->NewWindow();
615 child2->SetVisible(true);
616 root->AddChild(child2);
617 Window* original_focus = setup.window_tree_connection()->GetFocusedWindow();
618 Window* new_focus = child1;
619 ASSERT_NE(new_focus, original_focus);
620 new_focus->SetFocus();
621 ASSERT_TRUE(new_focus->HasFocus());
622 uint32_t change_id;
623 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
624
625 // Simulate the server responding with a focus change.
626 setup.window_tree_client()->OnWindowFocused(server_id(child2));
627
628 // This shouldn't trigger focus changing yet.
629 EXPECT_TRUE(child1->HasFocus());
630
631 // Tell the client the change failed, which should trigger failing to the
632 // most recent focus from server.
633 setup.window_tree_client()->OnChangeCompleted(change_id, false);
634 EXPECT_FALSE(child1->HasFocus());
635 EXPECT_TRUE(child2->HasFocus());
636 EXPECT_EQ(child2, setup.window_tree_connection()->GetFocusedWindow());
637
638 // Simulate server changing focus to child1. Should take immediately.
639 setup.window_tree_client()->OnWindowFocused(server_id(child1));
640 EXPECT_TRUE(child1->HasFocus());
641 }
642
643 TEST_F(WindowTreeClientImplTest, FocusOnRemovedWindowWithInFlightFocusChange) {
644 WindowTreeSetup setup;
645 Window* root = setup.GetFirstRoot();
646 ASSERT_TRUE(root);
647 root->SetVisible(true);
648 Window* child1 = setup.window_tree_connection()->NewWindow();
649 child1->SetVisible(true);
650 root->AddChild(child1);
651 Window* child2 = setup.window_tree_connection()->NewWindow();
652 child2->SetVisible(true);
653 root->AddChild(child2);
654
655 child1->SetFocus();
656 uint32_t change_id;
657 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
658
659 // Destroy child1, which should set focus to null.
660 child1->Destroy();
661 EXPECT_EQ(nullptr, setup.window_tree_connection()->GetFocusedWindow());
662
663 // Server changes focus to 2.
664 setup.window_tree_client()->OnWindowFocused(server_id(child2));
665 // Shouldn't take immediately.
666 EXPECT_FALSE(child2->HasFocus());
667
668 // Ack the change, focus should still be null.
669 setup.window_tree_client()->OnChangeCompleted(change_id, true);
670 EXPECT_EQ(nullptr, setup.window_tree_connection()->GetFocusedWindow());
671
672 // Change to 2 again, this time it should take.
673 setup.window_tree_client()->OnWindowFocused(server_id(child2));
674 EXPECT_TRUE(child2->HasFocus());
675 }
676
677 class ToggleVisibilityFromDestroyedObserver : public WindowObserver {
678 public:
679 explicit ToggleVisibilityFromDestroyedObserver(Window* window)
680 : window_(window) {
681 window_->AddObserver(this);
682 }
683
684 ToggleVisibilityFromDestroyedObserver() { EXPECT_FALSE(window_); }
685
686 // WindowObserver:
687 void OnWindowDestroyed(Window* window) override {
688 window_->SetVisible(!window->visible());
689 window_->RemoveObserver(this);
690 window_ = nullptr;
691 }
692
693 private:
694 Window* window_;
695
696 DISALLOW_COPY_AND_ASSIGN(ToggleVisibilityFromDestroyedObserver);
697 };
698
699 TEST_F(WindowTreeClientImplTest, ToggleVisibilityFromWindowDestroyed) {
700 WindowTreeSetup setup;
701 Window* root = setup.GetFirstRoot();
702 ASSERT_TRUE(root);
703 Window* child1 = setup.window_tree_connection()->NewWindow();
704 root->AddChild(child1);
705 ToggleVisibilityFromDestroyedObserver toggler(child1);
706 // Destroying the window triggers
707 // ToggleVisibilityFromDestroyedObserver::OnWindowDestroyed(), which toggles
708 // the visibility of the window. Ack the change, which should not crash or
709 // trigger DCHECKs.
710 child1->Destroy();
711 uint32_t change_id;
712 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
713 setup.window_tree_client()->OnChangeCompleted(change_id, true);
714 }
715
716 TEST_F(WindowTreeClientImplTest, NewTopLevelWindow) {
717 WindowTreeSetup setup;
718 Window* root1 = setup.GetFirstRoot();
719 ASSERT_TRUE(root1);
720 Window* root2 = setup.window_tree_connection()->NewTopLevelWindow(nullptr);
721 ASSERT_TRUE(root2);
722 EXPECT_TRUE(WindowPrivate(root2).parent_drawn());
723 ASSERT_NE(root2, root1);
724 EXPECT_NE(server_id(root2), server_id(root1));
725 EXPECT_EQ(2u, setup.window_tree_connection()->GetRoots().size());
726 EXPECT_TRUE(setup.window_tree_connection()->GetRoots().count(root1) > 0u);
727 EXPECT_TRUE(setup.window_tree_connection()->GetRoots().count(root2) > 0u);
728
729 // Ack the request to the windowtree to create the new window.
730 uint32_t change_id;
731 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
732 EXPECT_EQ(setup.window_tree()->window_id(), server_id(root2));
733
734 mojom::WindowDataPtr data = mojom::WindowData::New();
735 data->window_id = server_id(root2);
736 data->display_id = 1;
737 setup.window_tree_client()->OnTopLevelCreated(change_id, std::move(data),
738 false);
739
740 EXPECT_FALSE(WindowPrivate(root2).parent_drawn());
741
742 // Should not be able to add a top level as a child of another window.
743 root1->AddChild(root2);
744 ASSERT_EQ(nullptr, root2->parent());
745
746 // Destroy the first root, shouldn't initiate tear down.
747 root1->Destroy();
748 root1 = nullptr;
749 EXPECT_EQ(1u, setup.window_tree_connection()->GetRoots().size());
750 EXPECT_TRUE(setup.window_tree_connection()->GetRoots().count(root2) > 0u);
751 }
752
753 TEST_F(WindowTreeClientImplTest, NewTopLevelWindowGetsPropertiesFromData) {
754 WindowTreeSetup setup;
755 Window* root1 = setup.GetFirstRoot();
756 ASSERT_TRUE(root1);
757 Window* root2 = setup.window_tree_connection()->NewTopLevelWindow(nullptr);
758 ASSERT_TRUE(root2);
759
760 EXPECT_FALSE(root2->IsDrawn());
761 EXPECT_FALSE(root2->visible());
762
763 // Ack the request to the windowtree to create the new window.
764 uint32_t change_id;
765 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
766 EXPECT_EQ(setup.window_tree()->window_id(), server_id(root2));
767
768 mojom::WindowDataPtr data = mojom::WindowData::New();
769 data->window_id = server_id(root2);
770 data->display_id = 1;
771 data->bounds = mojo::Rect::From(gfx::Rect(1, 2, 3, 4));
772 data->visible = true;
773 setup.window_tree_client()->OnTopLevelCreated(change_id, std::move(data),
774 true);
775
776 // Make sure all the properties took.
777 EXPECT_TRUE(root2->IsDrawn());
778 EXPECT_TRUE(root2->visible());
779 EXPECT_EQ(1, root2->display_id());
780 EXPECT_EQ(gfx::Rect(1, 2, 3, 4), root2->bounds());
781 }
782
783 TEST_F(WindowTreeClientImplTest, NewTopLevelWindowGetsAllChangesInFlight) {
784 WindowTreeSetup setup;
785 Window* root1 = setup.GetFirstRoot();
786 ASSERT_TRUE(root1);
787 Window* root2 = setup.window_tree_connection()->NewTopLevelWindow(nullptr);
788 ASSERT_TRUE(root2);
789
790 EXPECT_FALSE(root2->IsDrawn());
791 EXPECT_FALSE(root2->visible());
792
793 // Get the id of the in flight change for creating the new window.
794 uint32_t new_window_in_flight_change_id;
795 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(
796 &new_window_in_flight_change_id));
797 EXPECT_EQ(setup.window_tree()->window_id(), server_id(root2));
798
799 // Make visibility go from false->true->false. Don't ack immediately.
800 root2->SetVisible(true);
801 uint32_t vis_in_flight_change_id1;
802 ASSERT_TRUE(
803 setup.window_tree()->GetAndClearChangeId(&vis_in_flight_change_id1));
804 EXPECT_NE(new_window_in_flight_change_id, vis_in_flight_change_id1);
805 root2->SetVisible(false);
806 uint32_t vis_in_flight_change_id2;
807 ASSERT_TRUE(
808 setup.window_tree()->GetAndClearChangeId(&vis_in_flight_change_id2));
809 EXPECT_NE(vis_in_flight_change_id1, vis_in_flight_change_id2);
810
811 // Change bounds to 5, 6, 7, 8.
812 root2->SetBounds(gfx::Rect(5, 6, 7, 8));
813 uint32_t bounds_in_flight_change_id;
814 ASSERT_TRUE(
815 setup.window_tree()->GetAndClearChangeId(&bounds_in_flight_change_id));
816 EXPECT_NE(vis_in_flight_change_id2, bounds_in_flight_change_id);
817
818 root2->SetSharedProperty<std::string>("xx", "client_xx");
819 uint32_t property_in_flight_change_id;
820 ASSERT_TRUE(
821 setup.window_tree()->GetAndClearChangeId(&property_in_flight_change_id));
822 EXPECT_NE(bounds_in_flight_change_id, property_in_flight_change_id);
823
824 // Ack the new window top level window. Vis and bounds shouldn't change.
825 mojom::WindowDataPtr data = mojom::WindowData::New();
826 data->window_id = server_id(root2);
827 data->bounds = mojo::Rect::From(gfx::Rect(1, 2, 3, 4));
828 data->display_id = 1;
829 data->visible = true;
830 data->properties["xx"] = mojo::Array<uint8_t>::From(std::string("server_xx"));
831 data->properties["yy"] = mojo::Array<uint8_t>::From(std::string("server_yy"));
832 setup.window_tree_client()->OnTopLevelCreated(new_window_in_flight_change_id,
833 std::move(data), true);
834
835 // The only value that should take effect is the property for 'yy' as it was
836 // not in flight.
837 EXPECT_TRUE(WindowPrivate(root2).parent_drawn());
838 EXPECT_FALSE(root2->visible());
839 EXPECT_EQ(1, root2->display_id());
840 EXPECT_EQ(gfx::Rect(5, 6, 7, 8), root2->bounds());
841 EXPECT_EQ(2u, root2->shared_properties().size());
842 ASSERT_TRUE(root2->HasSharedProperty("yy"));
843 EXPECT_EQ("server_yy", root2->GetSharedProperty<std::string>("yy"));
844 ASSERT_TRUE(root2->HasSharedProperty("xx"));
845 EXPECT_EQ("client_xx", root2->GetSharedProperty<std::string>("xx"));
846
847 // Tell the client the changes failed. This should cause the values to change
848 // to that of the server.
849 setup.window_tree_client()->OnChangeCompleted(vis_in_flight_change_id1,
850 false);
851 EXPECT_FALSE(root2->visible());
852 setup.window_tree_client()->OnChangeCompleted(vis_in_flight_change_id2,
853 false);
854 EXPECT_TRUE(root2->visible());
855 setup.window_tree_client()->OnChangeCompleted(bounds_in_flight_change_id,
856 false);
857 EXPECT_EQ(gfx::Rect(1, 2, 3, 4), root2->bounds());
858 setup.window_tree_client()->OnChangeCompleted(property_in_flight_change_id,
859 false);
860 EXPECT_EQ(2u, root2->shared_properties().size());
861 ASSERT_TRUE(root2->HasSharedProperty("yy"));
862 EXPECT_EQ("server_yy", root2->GetSharedProperty<std::string>("yy"));
863 ASSERT_TRUE(root2->HasSharedProperty("xx"));
864 EXPECT_EQ("server_xx", root2->GetSharedProperty<std::string>("xx"));
865 }
866
867 // Tests that if the client has multiple unowned windows, and one of them is a
868 // transient child to another, the teardown can happen cleanly.
869 TEST_F(WindowTreeClientImplTest, MultipleUnOwnedWindowsDuringDestruction) {
870 std::unique_ptr<WindowTreeSetup> setup(new WindowTreeSetup());
871 Window* root1 = setup->GetFirstRoot();
872 ASSERT_TRUE(root1);
873 Window* root2 = setup->window_tree_connection()->NewTopLevelWindow(nullptr);
874 ASSERT_TRUE(root2);
875 root1->AddTransientWindow(root2);
876
877 WindowTracker tracker;
878 tracker.Add(root1);
879 tracker.Add(root2);
880 setup.reset();
881 EXPECT_TRUE(tracker.windows().empty());
882 }
883
884 TEST_F(WindowTreeClientImplTest, TopLevelWindowDestroyedBeforeCreateComplete) {
885 WindowTreeSetup setup;
886 Window* root1 = setup.GetFirstRoot();
887 ASSERT_TRUE(root1);
888 Window* root2 = setup.window_tree_connection()->NewTopLevelWindow(nullptr);
889 ASSERT_TRUE(root2);
890 ASSERT_EQ(2u, setup.window_tree_connection()->GetRoots().size());
891
892 // Get the id of the in flight change for creating the new window.
893 uint32_t change_id;
894 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id));
895 EXPECT_EQ(setup.window_tree()->window_id(), server_id(root2));
896
897 mojom::WindowDataPtr data = mojom::WindowData::New();
898 data->window_id = server_id(root2);
899 data->display_id = 1;
900
901 // Destroy the window before the server has a chance to ack the window
902 // creation.
903 root2->Destroy();
904 EXPECT_EQ(1u, setup.window_tree_connection()->GetRoots().size());
905
906 setup.window_tree_client()->OnTopLevelCreated(change_id, std::move(data),
907 true);
908 EXPECT_EQ(1u, setup.window_tree_connection()->GetRoots().size());
909 }
910
911 // Tests both SetCapture and ReleaseCapture, to ensure that Window is properly
912 // updated on failures.
913 TEST_F(WindowTreeClientImplTest, ExplicitCapture) {
914 WindowTreeSetup setup;
915 Window* root = setup.GetFirstRoot();
916 ASSERT_TRUE(root);
917
918 root->SetCapture();
919 EXPECT_TRUE(root->HasCapture());
920 uint32_t change_id1;
921 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1));
922 setup.window_tree_client()->OnChangeCompleted(change_id1, false);
923 EXPECT_FALSE(root->HasCapture());
924
925 root->SetCapture();
926 EXPECT_TRUE(root->HasCapture());
927 uint32_t change_id2;
928 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2));
929 setup.window_tree_client()->OnChangeCompleted(change_id2, true);
930 EXPECT_TRUE(root->HasCapture());
931
932 root->ReleaseCapture();
933 EXPECT_FALSE(root->HasCapture());
934 uint32_t change_id3;
935 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id3));
936 setup.window_tree_client()->OnChangeCompleted(change_id3, false);
937 EXPECT_TRUE(root->HasCapture());
938
939 root->ReleaseCapture();
940 uint32_t change_id4;
941 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id4));
942 setup.window_tree_client()->OnChangeCompleted(change_id4, true);
943 EXPECT_FALSE(root->HasCapture());
944 }
945
946 // Tests that when capture is lost, that the window tree updates properly.
947 TEST_F(WindowTreeClientImplTest, LostCapture) {
948 WindowTreeSetup setup;
949 Window* root = setup.GetFirstRoot();
950 ASSERT_TRUE(root);
951
952 root->SetCapture();
953 EXPECT_TRUE(root->HasCapture());
954 uint32_t change_id1;
955 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1));
956 setup.window_tree_client()->OnChangeCompleted(change_id1, true);
957 EXPECT_TRUE(root->HasCapture());
958
959 // The second SetCapture should be ignored.
960 root->SetCapture();
961 uint32_t change_id2;
962 ASSERT_FALSE(setup.window_tree()->GetAndClearChangeId(&change_id2));
963
964 setup.window_tree_client()->OnLostCapture(server_id(root));
965 EXPECT_FALSE(root->HasCapture());
966 }
967
968 // Tests that when capture is lost, while there is a release capture request
969 // inflight, that the revert value of that request is updated correctly.
970 TEST_F(WindowTreeClientImplTest, LostCaptureDifferentInFlightChange) {
971 WindowTreeSetup setup;
972 Window* root = setup.GetFirstRoot();
973 ASSERT_TRUE(root);
974
975 root->SetCapture();
976 EXPECT_TRUE(root->HasCapture());
977 uint32_t change_id1;
978 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1));
979 setup.window_tree_client()->OnChangeCompleted(change_id1, true);
980 EXPECT_TRUE(root->HasCapture());
981
982 // The ReleaseCapture should be updated to the revert of the SetCapture.
983 root->ReleaseCapture();
984 uint32_t change_id2;
985 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2));
986
987 setup.window_tree_client()->OnLostCapture(server_id(root));
988 EXPECT_FALSE(root->HasCapture());
989
990 setup.window_tree_client()->OnChangeCompleted(change_id2, false);
991 EXPECT_FALSE(root->HasCapture());
992 }
993
994 // Tests that while two windows can inflight capture requests, that the
995 // WindowTreeClient only identifies one as having the current capture.
996 TEST_F(WindowTreeClientImplTest, TwoWindowsRequestCapture) {
997 WindowTreeSetup setup;
998 Window* root = setup.GetFirstRoot();
999 Window* child = setup.window_tree_connection()->NewWindow();
1000 child->SetVisible(true);
1001 root->AddChild(child);
1002
1003 root->SetCapture();
1004 EXPECT_TRUE(root->HasCapture());
1005 uint32_t change_id1;
1006 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1));
1007
1008 child->SetCapture();
1009 EXPECT_TRUE(child->HasCapture());
1010 EXPECT_FALSE(root->HasCapture());
1011
1012 uint32_t change_id2;
1013 ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2));
1014
1015 setup.window_tree_client()->OnChangeCompleted(change_id1, true);
1016 EXPECT_FALSE(root->HasCapture());
1017 EXPECT_TRUE(child->HasCapture());
1018
1019 setup.window_tree_client()->OnChangeCompleted(change_id2, false);
1020 EXPECT_FALSE(child->HasCapture());
1021 EXPECT_TRUE(root->HasCapture());
1022
1023 setup.window_tree_client()->OnLostCapture(server_id(root));
1024 EXPECT_FALSE(root->HasCapture());
1025 }
1026
1027 TEST_F(WindowTreeClientImplTest, WindowDestroyedWhileTransientChildHasCapture) {
1028 WindowTreeSetup setup;
1029 Window* root = setup.GetFirstRoot();
1030 Window* transient_parent = setup.window_tree_connection()->NewWindow();
1031 Window* transient_child = setup.window_tree_connection()->NewWindow();
1032 transient_parent->SetVisible(true);
1033 transient_child->SetVisible(true);
1034 root->AddChild(transient_parent);
1035 root->AddChild(transient_child);
1036
1037 transient_parent->AddTransientWindow(transient_child);
1038
1039 WindowTracker tracker;
1040 tracker.Add(transient_parent);
1041 tracker.Add(transient_child);
1042 // Request a capture on the transient child, then destroy the transient
1043 // parent. That will destroy both windows, and should reset the capture window
1044 // correctly.
1045 transient_child->SetCapture();
1046 transient_parent->Destroy();
1047 EXPECT_TRUE(tracker.windows().empty());
1048
1049 // Create a new Window, and attempt to place capture on that.
1050 Window* child = setup.window_tree_connection()->NewWindow();
1051 child->SetVisible(true);
1052 root->AddChild(child);
1053 child->SetCapture();
1054 EXPECT_TRUE(child->HasCapture());
1055 }
1056
1057 } // namespace mus
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698