blob: 0aaccf4d119b98b15c3f64fabc6e18249ad542ef [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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
Viet-Trung Luu15a59a82015-10-10 01:11:00 -07005#include "mojo/services/view_manager/cpp/view_manager.h"
James Robinson646469d2014-10-03 15:33:28 -07006
Michael Wasserman298bd9a2015-02-24 12:19:25 -08007#include "base/bind.h"
8#include "base/location.h"
James Robinson646469d2014-10-03 15:33:28 -07009#include "base/logging.h"
James Robinson3aefd1b2015-02-10 17:11:46 -080010#include "base/memory/scoped_vector.h"
Michael Wasserman298bd9a2015-02-24 12:19:25 -080011#include "base/message_loop/message_loop.h"
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -080012#include "base/run_loop.h"
Michael Wasserman298bd9a2015-02-24 12:19:25 -080013#include "base/test/test_timeouts.h"
James Robinson646469d2014-10-03 15:33:28 -070014#include "mojo/public/cpp/application/application_connection.h"
15#include "mojo/public/cpp/application/application_delegate.h"
16#include "mojo/public/cpp/application/application_impl.h"
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -080017#include "mojo/public/cpp/application/application_test_base.h"
James Robinson646469d2014-10-03 15:33:28 -070018#include "mojo/public/cpp/application/service_provider_impl.h"
Viet-Trung Luu84765c42015-10-10 01:07:51 -070019#include "mojo/services/geometry/cpp/geometry_util.h"
Viet-Trung Luu15a59a82015-10-10 01:11:00 -070020#include "mojo/services/view_manager/cpp/lib/view_manager_client_impl.h"
21#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
22#include "mojo/services/view_manager/cpp/view_manager_context.h"
23#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
24#include "mojo/services/view_manager/cpp/view_observer.h"
James Robinson646469d2014-10-03 15:33:28 -070025
26namespace mojo {
John Abd-El-Maleked8d84d2014-10-23 14:27:08 -070027
James Robinson646469d2014-10-03 15:33:28 -070028namespace {
29
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -080030base::RunLoop* current_run_loop = nullptr;
James Robinson646469d2014-10-03 15:33:28 -070031
Michael Wasserman298bd9a2015-02-24 12:19:25 -080032void TimeoutRunLoop(const base::Closure& timeout_task, bool* timeout) {
33 CHECK(current_run_loop);
34 *timeout = true;
35 timeout_task.Run();
36}
37
38bool DoRunLoopWithTimeout() {
39 if (current_run_loop != nullptr)
40 return false;
41
42 bool timeout = false;
James Robinson646469d2014-10-03 15:33:28 -070043 base::RunLoop run_loop;
Michael Wasserman298bd9a2015-02-24 12:19:25 -080044 base::MessageLoop::current()->PostDelayedTask(
45 FROM_HERE, base::Bind(&TimeoutRunLoop, run_loop.QuitClosure(), &timeout),
46 TestTimeouts::action_timeout());
47
James Robinson646469d2014-10-03 15:33:28 -070048 current_run_loop = &run_loop;
49 current_run_loop->Run();
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -080050 current_run_loop = nullptr;
Michael Wasserman298bd9a2015-02-24 12:19:25 -080051 return !timeout;
James Robinson646469d2014-10-03 15:33:28 -070052}
53
54void QuitRunLoop() {
55 current_run_loop->Quit();
Michael Wasserman298bd9a2015-02-24 12:19:25 -080056 current_run_loop = nullptr;
James Robinson646469d2014-10-03 15:33:28 -070057}
58
James Robinson646469d2014-10-03 15:33:28 -070059class BoundsChangeObserver : public ViewObserver {
60 public:
Michael Wasserman298bd9a2015-02-24 12:19:25 -080061 explicit BoundsChangeObserver(View* view) : view_(view) {
62 view_->AddObserver(this);
63 }
64 ~BoundsChangeObserver() override { view_->RemoveObserver(this); }
James Robinson646469d2014-10-03 15:33:28 -070065
66 private:
67 // Overridden from ViewObserver:
James Robinsone1b30cf2014-10-21 12:25:40 -070068 void OnViewBoundsChanged(View* view,
John Abd-El-Maleked8d84d2014-10-23 14:27:08 -070069 const Rect& old_bounds,
70 const Rect& new_bounds) override {
James Robinson646469d2014-10-03 15:33:28 -070071 DCHECK_EQ(view, view_);
72 QuitRunLoop();
73 }
74
75 View* view_;
76
James Robinson3aefd1b2015-02-10 17:11:46 -080077 MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
James Robinson646469d2014-10-03 15:33:28 -070078};
79
Michael Wasserman298bd9a2015-02-24 12:19:25 -080080// Wait until the bounds of the supplied view change; returns false on timeout.
81bool WaitForBoundsToChange(View* view) {
James Robinson646469d2014-10-03 15:33:28 -070082 BoundsChangeObserver observer(view);
Michael Wasserman298bd9a2015-02-24 12:19:25 -080083 return DoRunLoopWithTimeout();
James Robinson646469d2014-10-03 15:33:28 -070084}
85
Michael Wasserman298bd9a2015-02-24 12:19:25 -080086// Spins a run loop until the tree beginning at |root| has |tree_size| views
James Robinson646469d2014-10-03 15:33:28 -070087// (including |root|).
88class TreeSizeMatchesObserver : public ViewObserver {
89 public:
90 TreeSizeMatchesObserver(View* tree, size_t tree_size)
Michael Wasserman298bd9a2015-02-24 12:19:25 -080091 : tree_(tree), tree_size_(tree_size) {
92 tree_->AddObserver(this);
93 }
94 ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); }
James Robinson646469d2014-10-03 15:33:28 -070095
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -080096 bool IsTreeCorrectSize() { return CountViews(tree_) == tree_size_; }
James Robinson646469d2014-10-03 15:33:28 -070097
98 private:
99 // Overridden from ViewObserver:
James Robinsone1b30cf2014-10-21 12:25:40 -0700100 void OnTreeChanged(const TreeChangeParams& params) override {
James Robinson646469d2014-10-03 15:33:28 -0700101 if (IsTreeCorrectSize())
102 QuitRunLoop();
103 }
104
105 size_t CountViews(const View* view) const {
106 size_t count = 1;
107 View::Children::const_iterator it = view->children().begin();
108 for (; it != view->children().end(); ++it)
109 count += CountViews(*it);
110 return count;
111 }
112
113 View* tree_;
114 size_t tree_size_;
115
James Robinson3aefd1b2015-02-10 17:11:46 -0800116 MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
James Robinson646469d2014-10-03 15:33:28 -0700117};
118
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800119// Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
120bool WaitForTreeSizeToMatch(View* view, size_t tree_size) {
James Robinson646469d2014-10-03 15:33:28 -0700121 TreeSizeMatchesObserver observer(view, tree_size);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800122 return observer.IsTreeCorrectSize() || DoRunLoopWithTimeout();
James Robinson646469d2014-10-03 15:33:28 -0700123}
124
James Robinson646469d2014-10-03 15:33:28 -0700125class OrderChangeObserver : public ViewObserver {
126 public:
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800127 OrderChangeObserver(View* view) : view_(view) { view_->AddObserver(this); }
James Robinsone1b30cf2014-10-21 12:25:40 -0700128 ~OrderChangeObserver() override { view_->RemoveObserver(this); }
James Robinson646469d2014-10-03 15:33:28 -0700129
130 private:
131 // Overridden from ViewObserver:
James Robinsone1b30cf2014-10-21 12:25:40 -0700132 void OnViewReordered(View* view,
133 View* relative_view,
134 OrderDirection direction) override {
James Robinson646469d2014-10-03 15:33:28 -0700135 DCHECK_EQ(view, view_);
136 QuitRunLoop();
137 }
138
139 View* view_;
140
James Robinson3aefd1b2015-02-10 17:11:46 -0800141 MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
James Robinson646469d2014-10-03 15:33:28 -0700142};
143
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800144// Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
145bool WaitForOrderChange(ViewManager* view_manager, View* view) {
James Robinson646469d2014-10-03 15:33:28 -0700146 OrderChangeObserver observer(view);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800147 return DoRunLoopWithTimeout();
James Robinson646469d2014-10-03 15:33:28 -0700148}
149
150// Tracks a view's destruction. Query is_valid() for current state.
151class ViewTracker : public ViewObserver {
152 public:
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800153 explicit ViewTracker(View* view) : view_(view) { view_->AddObserver(this); }
James Robinsone1b30cf2014-10-21 12:25:40 -0700154 ~ViewTracker() override {
James Robinson646469d2014-10-03 15:33:28 -0700155 if (view_)
156 view_->RemoveObserver(this);
157 }
158
159 bool is_valid() const { return !!view_; }
160
161 private:
162 // Overridden from ViewObserver:
James Robinsone1b30cf2014-10-21 12:25:40 -0700163 void OnViewDestroyed(View* view) override {
James Robinson646469d2014-10-03 15:33:28 -0700164 DCHECK_EQ(view, view_);
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800165 view_ = nullptr;
James Robinson646469d2014-10-03 15:33:28 -0700166 }
167
James Robinson646469d2014-10-03 15:33:28 -0700168 View* view_;
169
James Robinson3aefd1b2015-02-10 17:11:46 -0800170 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker);
James Robinson646469d2014-10-03 15:33:28 -0700171};
172
173} // namespace
174
175// ViewManager -----------------------------------------------------------------
176
177// These tests model synchronization of two peer connections to the view manager
178// service, that are given access to some root view.
179
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800180class ViewManagerTest : public test::ApplicationTestBase,
181 public ApplicationDelegate,
182 public ViewManagerDelegate {
James Robinson646469d2014-10-03 15:33:28 -0700183 public:
184 ViewManagerTest()
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800185 : most_recent_view_manager_(nullptr), window_manager_(nullptr) {}
James Robinson646469d2014-10-03 15:33:28 -0700186
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800187 // Overridden from ApplicationDelegate:
188 void Initialize(ApplicationImpl* app) override {
189 view_manager_client_factory_.reset(
190 new ViewManagerClientFactory(app->shell(), this));
Alhaad Gokhale65d987a2015-02-11 17:09:55 -0800191 }
192
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800193 // ApplicationDelegate implementation.
194 bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
195 connection->AddService(view_manager_client_factory_.get());
196 return true;
197 }
198
199 ViewManager* window_manager() { return window_manager_; }
200
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800201 // Embeds another version of the test app @ view; returns nullptr on timeout.
James Robinson646469d2014-10-03 15:33:28 -0700202 ViewManager* Embed(ViewManager* view_manager, View* view) {
Scott Violet50c08a32014-12-19 07:45:16 -0800203 DCHECK_EQ(view_manager, view->view_manager());
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800204 most_recent_view_manager_ = nullptr;
Nick Bray24d8e692015-03-10 12:53:01 -0700205 view->Embed(application_impl()->url());
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800206 if (!DoRunLoopWithTimeout())
207 return nullptr;
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800208 ViewManager* vm = nullptr;
209 std::swap(vm, most_recent_view_manager_);
210 return vm;
James Robinson646469d2014-10-03 15:33:28 -0700211 }
212
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800213 ApplicationDelegate* GetApplicationDelegate() override { return this; }
Alhaad Gokhale65d987a2015-02-11 17:09:55 -0800214
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800215 // Overridden from ViewManagerDelegate:
216 void OnEmbed(View* root,
217 InterfaceRequest<ServiceProvider> services,
218 ServiceProviderPtr exposed_services) override {
219 most_recent_view_manager_ = root->view_manager();
220 QuitRunLoop();
Alhaad Gokhale65d987a2015-02-11 17:09:55 -0800221 }
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800222 void OnViewManagerDisconnected(ViewManager* view_manager) override {}
James Robinson646469d2014-10-03 15:33:28 -0700223
224 private:
225 // Overridden from testing::Test:
Viet-Trung Luu153dd212014-10-21 14:41:15 -0700226 void SetUp() override {
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800227 ApplicationTestBase::SetUp();
James Robinson646469d2014-10-03 15:33:28 -0700228
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800229 view_manager_context_.reset(new ViewManagerContext(application_impl()));
Nick Bray24d8e692015-03-10 12:53:01 -0700230 view_manager_context_->Embed(application_impl()->url());
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800231 ASSERT_TRUE(DoRunLoopWithTimeout());
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800232 std::swap(window_manager_, most_recent_view_manager_);
James Robinson646469d2014-10-03 15:33:28 -0700233 }
234
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800235 // Overridden from testing::Test:
236 void TearDown() override { ApplicationTestBase::TearDown(); }
James Robinson646469d2014-10-03 15:33:28 -0700237
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800238 scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
James Robinson646469d2014-10-03 15:33:28 -0700239
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800240 scoped_ptr<ViewManagerContext> view_manager_context_;
241
James Robinson646469d2014-10-03 15:33:28 -0700242 // Used to receive the most recent view manager loaded by an embed action.
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800243 ViewManager* most_recent_view_manager_;
James Robinson646469d2014-10-03 15:33:28 -0700244 // The View Manager connection held by the window manager (app running at the
245 // root view).
246 ViewManager* window_manager_;
James Robinson646469d2014-10-03 15:33:28 -0700247
James Robinson3aefd1b2015-02-10 17:11:46 -0800248 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
James Robinson646469d2014-10-03 15:33:28 -0700249};
250
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800251TEST_F(ViewManagerTest, RootView) {
252 ASSERT_NE(nullptr, window_manager());
253 EXPECT_NE(nullptr, window_manager()->GetRoot());
254 EXPECT_EQ("mojo:window_manager", window_manager()->GetEmbedderURL());
255}
James Robinson646469d2014-10-03 15:33:28 -0700256
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800257TEST_F(ViewManagerTest, Embed) {
Scott Violet50c08a32014-12-19 07:45:16 -0800258 View* view = window_manager()->CreateView();
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800259 ASSERT_NE(nullptr, view);
Scott Violet0fb0af32014-11-20 13:04:25 -0800260 view->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800261 window_manager()->GetRoot()->AddChild(view);
James Robinson646469d2014-10-03 15:33:28 -0700262 ViewManager* embedded = Embed(window_manager(), view);
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800263 ASSERT_NE(nullptr, embedded);
James Robinson646469d2014-10-03 15:33:28 -0700264
Scott Violet8f7d8af2014-12-10 15:44:33 -0800265 View* view_in_embedded = embedded->GetRoot();
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800266 ASSERT_NE(nullptr, view_in_embedded);
267 EXPECT_EQ(view->id(), view_in_embedded->id());
268 EXPECT_EQ(nullptr, view_in_embedded->parent());
269 EXPECT_TRUE(view_in_embedded->children().empty());
James Robinson646469d2014-10-03 15:33:28 -0700270}
271
272// Window manager has two views, N1 and N11. Embeds A at N1. A should not see
273// N11.
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800274TEST_F(ViewManagerTest, EmbeddedDoesntSeeChild) {
Scott Violet50c08a32014-12-19 07:45:16 -0800275 View* view = window_manager()->CreateView();
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800276 ASSERT_NE(nullptr, view);
Scott Violet0fb0af32014-11-20 13:04:25 -0800277 view->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800278 window_manager()->GetRoot()->AddChild(view);
Scott Violet50c08a32014-12-19 07:45:16 -0800279 View* nested = window_manager()->CreateView();
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800280 ASSERT_NE(nullptr, nested);
Scott Violet0fb0af32014-11-20 13:04:25 -0800281 nested->SetVisible(true);
James Robinson646469d2014-10-03 15:33:28 -0700282 view->AddChild(nested);
283
284 ViewManager* embedded = Embed(window_manager(), view);
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800285 ASSERT_NE(nullptr, embedded);
286 View* view_in_embedded = embedded->GetRoot();
287 EXPECT_EQ(view->id(), view_in_embedded->id());
288 EXPECT_EQ(nullptr, view_in_embedded->parent());
289 EXPECT_TRUE(view_in_embedded->children().empty());
James Robinson646469d2014-10-03 15:33:28 -0700290}
291
292// TODO(beng): write a replacement test for the one that once existed here:
293// This test validates the following scenario:
294// - a view originating from one connection
295// - a view originating from a second connection
296// + the connection originating the view is destroyed
297// -> the view should still exist (since the second connection is live) but
298// should be disconnected from any views.
299// http://crbug.com/396300
300//
301// TODO(beng): The new test should validate the scenario as described above
302// except that the second connection still has a valid tree.
303
304// Verifies that bounds changes applied to a view hierarchy in one connection
305// are reflected to another.
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800306TEST_F(ViewManagerTest, SetBounds) {
Scott Violet50c08a32014-12-19 07:45:16 -0800307 View* view = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800308 view->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800309 window_manager()->GetRoot()->AddChild(view);
James Robinson646469d2014-10-03 15:33:28 -0700310 ViewManager* embedded = Embed(window_manager(), view);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800311 ASSERT_NE(nullptr, embedded);
James Robinson646469d2014-10-03 15:33:28 -0700312
313 View* view_in_embedded = embedded->GetViewById(view->id());
314 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
315
John Abd-El-Maleked8d84d2014-10-23 14:27:08 -0700316 Rect rect;
317 rect.width = rect.height = 100;
318 view->SetBounds(rect);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800319 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
James Robinson646469d2014-10-03 15:33:28 -0700320 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
321}
322
323// Verifies that bounds changes applied to a view owned by a different
324// connection are refused.
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800325TEST_F(ViewManagerTest, SetBoundsSecurity) {
Scott Violet50c08a32014-12-19 07:45:16 -0800326 View* view = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800327 view->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800328 window_manager()->GetRoot()->AddChild(view);
James Robinson646469d2014-10-03 15:33:28 -0700329 ViewManager* embedded = Embed(window_manager(), view);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800330 ASSERT_NE(nullptr, embedded);
James Robinson646469d2014-10-03 15:33:28 -0700331
332 View* view_in_embedded = embedded->GetViewById(view->id());
John Abd-El-Maleked8d84d2014-10-23 14:27:08 -0700333 Rect rect;
334 rect.width = 800;
335 rect.height = 600;
336 view->SetBounds(rect);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800337 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
James Robinson646469d2014-10-03 15:33:28 -0700338
John Abd-El-Maleked8d84d2014-10-23 14:27:08 -0700339 rect.width = 1024;
340 rect.height = 768;
341 view_in_embedded->SetBounds(rect);
James Robinson646469d2014-10-03 15:33:28 -0700342 // Bounds change should have been rejected.
343 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
344}
345
346// Verifies that a view can only be destroyed by the connection that created it.
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800347TEST_F(ViewManagerTest, DestroySecurity) {
Scott Violet50c08a32014-12-19 07:45:16 -0800348 View* view = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800349 view->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800350 window_manager()->GetRoot()->AddChild(view);
James Robinson646469d2014-10-03 15:33:28 -0700351 ViewManager* embedded = Embed(window_manager(), view);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800352 ASSERT_NE(nullptr, embedded);
James Robinson646469d2014-10-03 15:33:28 -0700353
354 View* view_in_embedded = embedded->GetViewById(view->id());
355
356 ViewTracker tracker2(view_in_embedded);
357 view_in_embedded->Destroy();
358 // View should not have been destroyed.
359 EXPECT_TRUE(tracker2.is_valid());
360
361 ViewTracker tracker1(view);
362 view->Destroy();
363 EXPECT_FALSE(tracker1.is_valid());
364}
365
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800366TEST_F(ViewManagerTest, MultiRoots) {
Scott Violet50c08a32014-12-19 07:45:16 -0800367 View* view1 = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800368 view1->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800369 window_manager()->GetRoot()->AddChild(view1);
Scott Violet50c08a32014-12-19 07:45:16 -0800370 View* view2 = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800371 view2->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800372 window_manager()->GetRoot()->AddChild(view2);
James Robinson646469d2014-10-03 15:33:28 -0700373 ViewManager* embedded1 = Embed(window_manager(), view1);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800374 ASSERT_NE(nullptr, embedded1);
James Robinson646469d2014-10-03 15:33:28 -0700375 ViewManager* embedded2 = Embed(window_manager(), view2);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800376 ASSERT_NE(nullptr, embedded2);
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800377 EXPECT_NE(embedded1, embedded2);
James Robinson646469d2014-10-03 15:33:28 -0700378}
379
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800380TEST_F(ViewManagerTest, EmbeddingIdentity) {
Scott Violet50c08a32014-12-19 07:45:16 -0800381 View* view = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800382 view->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800383 window_manager()->GetRoot()->AddChild(view);
James Robinson646469d2014-10-03 15:33:28 -0700384 ViewManager* embedded = Embed(window_manager(), view);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800385 ASSERT_NE(nullptr, embedded);
Nick Bray24d8e692015-03-10 12:53:01 -0700386 EXPECT_EQ(application_impl()->url(), embedded->GetEmbedderURL());
James Robinson646469d2014-10-03 15:33:28 -0700387}
388
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800389// TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
390// Debug and re-enable this.
James Robinson646469d2014-10-03 15:33:28 -0700391TEST_F(ViewManagerTest, DISABLED_Reorder) {
Scott Violet50c08a32014-12-19 07:45:16 -0800392 View* view1 = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800393 view1->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800394 window_manager()->GetRoot()->AddChild(view1);
James Robinson646469d2014-10-03 15:33:28 -0700395
396 ViewManager* embedded = Embed(window_manager(), view1);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800397 ASSERT_NE(nullptr, embedded);
James Robinson646469d2014-10-03 15:33:28 -0700398
Scott Violet50c08a32014-12-19 07:45:16 -0800399 View* view11 = embedded->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800400 view11->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800401 embedded->GetRoot()->AddChild(view11);
Scott Violet50c08a32014-12-19 07:45:16 -0800402 View* view12 = embedded->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800403 view12->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800404 embedded->GetRoot()->AddChild(view12);
James Robinson646469d2014-10-03 15:33:28 -0700405
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800406 View* root_in_embedded = embedded->GetRoot();
James Robinson646469d2014-10-03 15:33:28 -0700407
408 {
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800409 ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded, 3u));
James Robinson646469d2014-10-03 15:33:28 -0700410 view11->MoveToFront();
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800411 ASSERT_TRUE(WaitForOrderChange(embedded, root_in_embedded));
James Robinson646469d2014-10-03 15:33:28 -0700412
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800413 EXPECT_EQ(root_in_embedded->children().front(),
414 embedded->GetViewById(view12->id()));
415 EXPECT_EQ(root_in_embedded->children().back(),
416 embedded->GetViewById(view11->id()));
James Robinson646469d2014-10-03 15:33:28 -0700417 }
418
419 {
420 view11->MoveToBack();
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800421 ASSERT_TRUE(WaitForOrderChange(embedded,
422 embedded->GetViewById(view11->id())));
James Robinson646469d2014-10-03 15:33:28 -0700423
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800424 EXPECT_EQ(root_in_embedded->children().front(),
425 embedded->GetViewById(view11->id()));
426 EXPECT_EQ(root_in_embedded->children().back(),
427 embedded->GetViewById(view12->id()));
James Robinson646469d2014-10-03 15:33:28 -0700428 }
429}
430
431namespace {
432
433class VisibilityChangeObserver : public ViewObserver {
434 public:
435 explicit VisibilityChangeObserver(View* view) : view_(view) {
436 view_->AddObserver(this);
437 }
James Robinsone1b30cf2014-10-21 12:25:40 -0700438 ~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
James Robinson646469d2014-10-03 15:33:28 -0700439
440 private:
441 // Overridden from ViewObserver:
James Robinsone1b30cf2014-10-21 12:25:40 -0700442 void OnViewVisibilityChanged(View* view) override {
James Robinson646469d2014-10-03 15:33:28 -0700443 EXPECT_EQ(view, view_);
444 QuitRunLoop();
445 }
446
447 View* view_;
448
James Robinson3aefd1b2015-02-10 17:11:46 -0800449 MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
James Robinson646469d2014-10-03 15:33:28 -0700450};
451
452} // namespace
453
Scott Violet3d23dae2015-02-23 10:23:26 -0800454TEST_F(ViewManagerTest, Visible) {
Scott Violet50c08a32014-12-19 07:45:16 -0800455 View* view1 = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800456 view1->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800457 window_manager()->GetRoot()->AddChild(view1);
James Robinson646469d2014-10-03 15:33:28 -0700458
459 // Embed another app and verify initial state.
460 ViewManager* embedded = Embed(window_manager(), view1);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800461 ASSERT_NE(nullptr, embedded);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800462 ASSERT_NE(nullptr, embedded->GetRoot());
463 View* embedded_root = embedded->GetRoot();
James Robinson646469d2014-10-03 15:33:28 -0700464 EXPECT_TRUE(embedded_root->visible());
465 EXPECT_TRUE(embedded_root->IsDrawn());
466
467 // Change the visible state from the first connection and verify its mirrored
468 // correctly to the embedded app.
469 {
470 VisibilityChangeObserver observer(embedded_root);
471 view1->SetVisible(false);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800472 ASSERT_TRUE(DoRunLoopWithTimeout());
James Robinson646469d2014-10-03 15:33:28 -0700473 }
474
475 EXPECT_FALSE(view1->visible());
476 EXPECT_FALSE(view1->IsDrawn());
477
478 EXPECT_FALSE(embedded_root->visible());
479 EXPECT_FALSE(embedded_root->IsDrawn());
480
481 // Make the node visible again.
482 {
483 VisibilityChangeObserver observer(embedded_root);
484 view1->SetVisible(true);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800485 ASSERT_TRUE(DoRunLoopWithTimeout());
James Robinson646469d2014-10-03 15:33:28 -0700486 }
487
488 EXPECT_TRUE(view1->visible());
489 EXPECT_TRUE(view1->IsDrawn());
490
491 EXPECT_TRUE(embedded_root->visible());
492 EXPECT_TRUE(embedded_root->IsDrawn());
493}
494
495namespace {
496
497class DrawnChangeObserver : public ViewObserver {
498 public:
499 explicit DrawnChangeObserver(View* view) : view_(view) {
500 view_->AddObserver(this);
501 }
James Robinsone1b30cf2014-10-21 12:25:40 -0700502 ~DrawnChangeObserver() override { view_->RemoveObserver(this); }
James Robinson646469d2014-10-03 15:33:28 -0700503
504 private:
505 // Overridden from ViewObserver:
James Robinsone1b30cf2014-10-21 12:25:40 -0700506 void OnViewDrawnChanged(View* view) override {
James Robinson646469d2014-10-03 15:33:28 -0700507 EXPECT_EQ(view, view_);
508 QuitRunLoop();
509 }
510
511 View* view_;
512
James Robinson3aefd1b2015-02-10 17:11:46 -0800513 MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
James Robinson646469d2014-10-03 15:33:28 -0700514};
515
516} // namespace
517
Alhaad Gokhalee5f93b32015-02-12 14:28:31 -0800518TEST_F(ViewManagerTest, Drawn) {
Scott Violet50c08a32014-12-19 07:45:16 -0800519 View* view1 = window_manager()->CreateView();
Scott Violet0fb0af32014-11-20 13:04:25 -0800520 view1->SetVisible(true);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800521 window_manager()->GetRoot()->AddChild(view1);
James Robinson646469d2014-10-03 15:33:28 -0700522
523 // Embed another app and verify initial state.
524 ViewManager* embedded = Embed(window_manager(), view1);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800525 ASSERT_NE(nullptr, embedded);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800526 ASSERT_NE(nullptr, embedded->GetRoot());
527 View* embedded_root = embedded->GetRoot();
James Robinson646469d2014-10-03 15:33:28 -0700528 EXPECT_TRUE(embedded_root->visible());
529 EXPECT_TRUE(embedded_root->IsDrawn());
530
531 // Change the visibility of the root, this should propagate a drawn state
532 // change to |embedded|.
533 {
534 DrawnChangeObserver observer(embedded_root);
Scott Violet8f7d8af2014-12-10 15:44:33 -0800535 window_manager()->GetRoot()->SetVisible(false);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800536 ASSERT_TRUE(DoRunLoopWithTimeout());
James Robinson646469d2014-10-03 15:33:28 -0700537 }
538
539 EXPECT_TRUE(view1->visible());
540 EXPECT_FALSE(view1->IsDrawn());
541
542 EXPECT_TRUE(embedded_root->visible());
543 EXPECT_FALSE(embedded_root->IsDrawn());
544}
545
546// TODO(beng): tests for view event dispatcher.
547// - verify that we see events for all views.
548
Alhaad Gokhale83893692015-02-19 14:34:45 -0800549namespace {
James Robinson646469d2014-10-03 15:33:28 -0700550
Alhaad Gokhale83893692015-02-19 14:34:45 -0800551class FocusChangeObserver : public ViewObserver {
552 public:
553 explicit FocusChangeObserver(View* view)
554 : view_(view), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
555 view_->AddObserver(this);
556 }
557 ~FocusChangeObserver() override { view_->RemoveObserver(this); }
558
559 View* last_gained_focus() { return last_gained_focus_; }
560
561 View* last_lost_focus() { return last_lost_focus_; }
562
563 private:
564 // Overridden from ViewObserver.
565 void OnViewFocusChanged(View* gained_focus, View* lost_focus) override {
566 last_gained_focus_ = gained_focus;
567 last_lost_focus_ = lost_focus;
568 QuitRunLoop();
569 }
570
571 View* view_;
572 View* last_gained_focus_;
573 View* last_lost_focus_;
574
575 MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver);
576};
577
578} // namespace
579
Scott Violet6edabbf2015-02-25 16:02:50 -0800580TEST_F(ViewManagerTest, Focus) {
Alhaad Gokhale83893692015-02-19 14:34:45 -0800581 View* view1 = window_manager()->CreateView();
582 view1->SetVisible(true);
583 window_manager()->GetRoot()->AddChild(view1);
584
585 ViewManager* embedded = Embed(window_manager(), view1);
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800586 ASSERT_NE(nullptr, embedded);
Alhaad Gokhale83893692015-02-19 14:34:45 -0800587 View* view11 = embedded->CreateView();
588 view11->SetVisible(true);
589 embedded->GetRoot()->AddChild(view11);
590
591 // TODO(alhaad): Figure out why switching focus between views from different
592 // connections is causing the tests to crash and add tests for that.
593 {
594 View* embedded_root = embedded->GetRoot();
595 FocusChangeObserver observer(embedded_root);
596 embedded_root->SetFocus();
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800597 ASSERT_TRUE(DoRunLoopWithTimeout());
Alhaad Gokhale83893692015-02-19 14:34:45 -0800598 ASSERT_NE(nullptr, observer.last_gained_focus());
599 EXPECT_EQ(embedded_root->id(), observer.last_gained_focus()->id());
600 }
601 {
602 FocusChangeObserver observer(view11);
603 view11->SetFocus();
Michael Wasserman298bd9a2015-02-24 12:19:25 -0800604 ASSERT_TRUE(DoRunLoopWithTimeout());
Alhaad Gokhale83893692015-02-19 14:34:45 -0800605 ASSERT_NE(nullptr, observer.last_gained_focus());
606 ASSERT_NE(nullptr, observer.last_lost_focus());
607 EXPECT_EQ(view11->id(), observer.last_gained_focus()->id());
608 EXPECT_EQ(embedded->GetRoot()->id(), observer.last_lost_focus()->id());
609 }
610}
Scott Violetc320fa22014-11-19 14:05:54 -0800611
Mitch Rudominer6c98b312015-05-29 11:17:56 -0700612class ViewRemovedFromParentObserver : public ViewObserver {
613 public:
614 explicit ViewRemovedFromParentObserver(View* view)
615 : view_(view), was_removed_(false) {
616 view_->AddObserver(this);
617 }
618 ~ViewRemovedFromParentObserver() override { view_->RemoveObserver(this); }
619
620 bool was_removed() const { return was_removed_; }
621
622 private:
623 // Overridden from ViewObserver:
624 void OnTreeChanged(const TreeChangeParams& params) override {
625 if (params.target == view_ && !params.new_parent)
626 was_removed_ = true;
627 }
628
629 View* view_;
630 bool was_removed_;
631
632 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewRemovedFromParentObserver);
633};
634
635TEST_F(ViewManagerTest, EmbedRemovesChildren) {
636 View* view1 = window_manager()->CreateView();
637 View* view2 = window_manager()->CreateView();
638 window_manager()->GetRoot()->AddChild(view1);
639 view1->AddChild(view2);
640
641 ViewRemovedFromParentObserver observer(view2);
642 view1->Embed(application_impl()->url());
643 EXPECT_TRUE(observer.was_removed());
644 EXPECT_EQ(nullptr, view2->parent());
645 EXPECT_TRUE(view1->children().empty());
Mitch Rudominer1f186612015-06-01 18:15:57 -0700646
647 // Run the message loop so the Embed() call above completes. Without this
648 // we may end up reconnecting to the test and rerunning the test, which is
649 // problematic since the other services don't shut down.
650 ASSERT_TRUE(DoRunLoopWithTimeout());
Mitch Rudominer6c98b312015-05-29 11:17:56 -0700651}
652
James Robinson646469d2014-10-03 15:33:28 -0700653} // namespace mojo