1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "mojo/services/public/cpp/view_manager/view_manager.h"
6
7#include "base/auto_reset.h"
8#include "base/bind.h"
9#include "base/logging.h"
10#include "mojo/application_manager/application_manager.h"
11#include "mojo/public/cpp/application/application_connection.h"
12#include "mojo/public/cpp/application/application_delegate.h"
13#include "mojo/public/cpp/application/application_impl.h"
14#include "mojo/public/cpp/application/service_provider_impl.h"
15#include "mojo/public/interfaces/application/service_provider.mojom.h"
16#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
17#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
18#include "mojo/services/public/cpp/view_manager/util.h"
19#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
20#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
21#include "mojo/services/public/cpp/view_manager/view_observer.h"
22#include "mojo/shell/shell_test_helper.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25namespace mojo {
26namespace {
27
28const char kWindowManagerURL[] = "mojo:window_manager";
29const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
30
31base::RunLoop* current_run_loop = NULL;
32
33void DoRunLoop() {
34  base::RunLoop run_loop;
35  current_run_loop = &run_loop;
36  current_run_loop->Run();
37  current_run_loop = NULL;
38}
39
40void QuitRunLoop() {
41  current_run_loop->Quit();
42}
43
44class ConnectApplicationLoader : public ApplicationLoader,
45                                 public ApplicationDelegate,
46                                 public ViewManagerDelegate {
47 public:
48  typedef base::Callback<void(ViewManager*, View*)> LoadedCallback;
49
50  explicit ConnectApplicationLoader(const LoadedCallback& callback)
51      : callback_(callback) {}
52  virtual ~ConnectApplicationLoader() {}
53
54 private:
55  // Overridden from ApplicationDelegate:
56  virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
57    view_manager_client_factory_.reset(
58        new ViewManagerClientFactory(app->shell(), this));
59  }
60
61  // Overridden from ApplicationLoader:
62  virtual void Load(ApplicationManager* manager,
63                    const GURL& url,
64                    scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
65    ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
66    if (!shell_handle.is_valid())
67      return;
68    scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
69                                                        shell_handle.Pass()));
70    apps_.push_back(app.release());
71  }
72
73  virtual void OnApplicationError(ApplicationManager* manager,
74                                  const GURL& url) OVERRIDE {}
75
76  virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
77      OVERRIDE {
78    connection->AddService(view_manager_client_factory_.get());
79    return true;
80  }
81
82  // Overridden from ViewManagerDelegate:
83  virtual void OnEmbed(ViewManager* view_manager,
84                       View* root,
85                       ServiceProviderImpl* exported_services,
86                       scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
87    callback_.Run(view_manager, root);
88  }
89  virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {}
90
91  ScopedVector<ApplicationImpl> apps_;
92  LoadedCallback callback_;
93  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
94
95  DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader);
96};
97
98class BoundsChangeObserver : public ViewObserver {
99 public:
100  explicit BoundsChangeObserver(View* view) : view_(view) {}
101  virtual ~BoundsChangeObserver() {}
102
103 private:
104  // Overridden from ViewObserver:
105  virtual void OnViewBoundsChanged(View* view,
106                                   const gfx::Rect& old_bounds,
107                                   const gfx::Rect& new_bounds) OVERRIDE {
108    DCHECK_EQ(view, view_);
109    QuitRunLoop();
110  }
111
112  View* view_;
113
114  DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
115};
116
117// Wait until the bounds of the supplied view change.
118void WaitForBoundsToChange(View* view) {
119  BoundsChangeObserver observer(view);
120  view->AddObserver(&observer);
121  DoRunLoop();
122  view->RemoveObserver(&observer);
123}
124
125// Spins a runloop until the tree beginning at |root| has |tree_size| views
126// (including |root|).
127class TreeSizeMatchesObserver : public ViewObserver {
128 public:
129  TreeSizeMatchesObserver(View* tree, size_t tree_size)
130      : tree_(tree),
131        tree_size_(tree_size) {}
132  virtual ~TreeSizeMatchesObserver() {}
133
134  bool IsTreeCorrectSize() {
135    return CountViews(tree_) == tree_size_;
136  }
137
138 private:
139  // Overridden from ViewObserver:
140  virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE {
141    if (IsTreeCorrectSize())
142      QuitRunLoop();
143  }
144
145  size_t CountViews(const View* view) const {
146    size_t count = 1;
147    View::Children::const_iterator it = view->children().begin();
148    for (; it != view->children().end(); ++it)
149      count += CountViews(*it);
150    return count;
151  }
152
153  View* tree_;
154  size_t tree_size_;
155
156  DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
157};
158
159void WaitForTreeSizeToMatch(View* view, size_t tree_size) {
160  TreeSizeMatchesObserver observer(view, tree_size);
161  if (observer.IsTreeCorrectSize())
162    return;
163  view->AddObserver(&observer);
164  DoRunLoop();
165  view->RemoveObserver(&observer);
166}
167
168// Utility class that waits for the destruction of some number of views and
169// views.
170class DestructionObserver : public ViewObserver {
171 public:
172  // |views| or |views| can be NULL.
173  explicit DestructionObserver(std::set<Id>* views) : views_(views) {}
174
175 private:
176  // Overridden from ViewObserver:
177  virtual void OnViewDestroyed(View* view) OVERRIDE {
178    std::set<Id>::iterator it = views_->find(view->id());
179    if (it != views_->end())
180      views_->erase(it);
181    if (CanQuit())
182      QuitRunLoop();
183  }
184
185  bool CanQuit() {
186    return !views_ || views_->empty();
187  }
188
189  std::set<Id>* views_;
190
191  DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
192};
193
194void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) {
195  DestructionObserver observer(views);
196  DCHECK(views);
197  if (views) {
198    for (std::set<Id>::const_iterator it = views->begin();
199          it != views->end(); ++it) {
200      view_manager->GetViewById(*it)->AddObserver(&observer);
201    }
202  }
203  DoRunLoop();
204}
205
206class OrderChangeObserver : public ViewObserver {
207 public:
208  OrderChangeObserver(View* view) : view_(view) {
209    view_->AddObserver(this);
210  }
211  virtual ~OrderChangeObserver() {
212    view_->RemoveObserver(this);
213  }
214
215 private:
216  // Overridden from ViewObserver:
217  virtual void OnViewReordered(View* view,
218                               View* relative_view,
219                               OrderDirection direction) OVERRIDE {
220    DCHECK_EQ(view, view_);
221    QuitRunLoop();
222  }
223
224  View* view_;
225
226  DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
227};
228
229void WaitForOrderChange(ViewManager* view_manager, View* view) {
230  OrderChangeObserver observer(view);
231  DoRunLoop();
232}
233
234// Tracks a view's destruction. Query is_valid() for current state.
235class ViewTracker : public ViewObserver {
236 public:
237  explicit ViewTracker(View* view) : view_(view) {
238    view_->AddObserver(this);
239  }
240  virtual ~ViewTracker() {
241    if (view_)
242      view_->RemoveObserver(this);
243  }
244
245  bool is_valid() const { return !!view_; }
246
247 private:
248  // Overridden from ViewObserver:
249  virtual void OnViewDestroyed(View* view) OVERRIDE {
250    DCHECK_EQ(view, view_);
251    view_ = NULL;
252  }
253
254  int id_;
255  View* view_;
256
257  DISALLOW_COPY_AND_ASSIGN(ViewTracker);
258};
259
260}  // namespace
261
262// ViewManager -----------------------------------------------------------------
263
264// These tests model synchronization of two peer connections to the view manager
265// service, that are given access to some root view.
266
267class ViewManagerTest : public testing::Test {
268 public:
269  ViewManagerTest()
270      : connect_loop_(NULL),
271        loaded_view_manager_(NULL),
272        window_manager_(NULL),
273        commit_count_(0) {}
274
275 protected:
276  ViewManager* window_manager() { return window_manager_; }
277
278  View* CreateViewInParent(View* parent) {
279    ViewManager* parent_manager = ViewPrivate(parent).view_manager();
280    View* view = View::Create(parent_manager);
281    parent->AddChild(view);
282    return view;
283  }
284
285  // Embeds another version of the test app @ view.
286  ViewManager* Embed(ViewManager* view_manager, View* view) {
287    DCHECK_EQ(view_manager, ViewPrivate(view).view_manager());
288    view->Embed(kEmbeddedApp1URL);
289    RunRunLoop();
290    return GetLoadedViewManager();
291  }
292
293  ViewManager* GetLoadedViewManager() {
294    ViewManager* view_manager = loaded_view_manager_;
295    loaded_view_manager_ = NULL;
296    return view_manager;
297  }
298
299  void UnloadApplication(const GURL& url) {
300    test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url);
301  }
302
303 private:
304  // Overridden from testing::Test:
305  virtual void SetUp() OVERRIDE {
306    ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind(
307        &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this));
308    test_helper_.Init();
309    test_helper_.SetLoaderForURL(
310        scoped_ptr<ApplicationLoader>(
311            new ConnectApplicationLoader(ready_callback)),
312        GURL(kWindowManagerURL));
313    test_helper_.SetLoaderForURL(
314        scoped_ptr<ApplicationLoader>(
315            new ConnectApplicationLoader(ready_callback)),
316        GURL(kEmbeddedApp1URL));
317
318    test_helper_.application_manager()->ConnectToService(
319        GURL("mojo:mojo_view_manager"), &view_manager_init_);
320    ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL));
321  }
322
323  void EmbedRootCallback(bool* result_cache, bool result) {
324    *result_cache = result;
325  }
326
327  bool EmbedRoot(ViewManagerInitService* view_manager_init,
328                 const std::string& url) {
329    bool result = false;
330    ServiceProviderPtr sp;
331    BindToProxy(new ServiceProviderImpl, &sp);
332    view_manager_init->Embed(
333        url, sp.Pass(),
334        base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this),
335                   &result));
336    RunRunLoop();
337    window_manager_ = GetLoadedViewManager();
338    return result;
339  }
340
341  void OnViewManagerLoaded(ViewManager* view_manager, View* root) {
342    loaded_view_manager_ = view_manager;
343    connect_loop_->Quit();
344  }
345
346  void RunRunLoop() {
347    base::RunLoop run_loop;
348    connect_loop_ = &run_loop;
349    connect_loop_->Run();
350    connect_loop_ = NULL;
351  }
352
353  base::RunLoop* connect_loop_;
354  shell::ShellTestHelper test_helper_;
355  ViewManagerInitServicePtr view_manager_init_;
356  // Used to receive the most recent view manager loaded by an embed action.
357  ViewManager* loaded_view_manager_;
358  // The View Manager connection held by the window manager (app running at the
359  // root view).
360  ViewManager* window_manager_;
361  int commit_count_;
362
363  DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
364};
365
366TEST_F(ViewManagerTest, SetUp) {}
367
368TEST_F(ViewManagerTest, Embed) {
369  View* view = View::Create(window_manager());
370  window_manager()->GetRoots().front()->AddChild(view);
371  ViewManager* embedded = Embed(window_manager(), view);
372  EXPECT_TRUE(NULL != embedded);
373
374  View* view_in_embedded = embedded->GetRoots().front();
375  EXPECT_EQ(view->parent(), window_manager()->GetRoots().front());
376  EXPECT_EQ(NULL, view_in_embedded->parent());
377}
378
379// Window manager has two views, N1 and N11. Embeds A at N1. A should not see
380// N11.
381// TODO(sky): Update client lib to match server.
382TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) {
383  View* view = View::Create(window_manager());
384  window_manager()->GetRoots().front()->AddChild(view);
385  View* nested = View::Create(window_manager());
386  view->AddChild(nested);
387
388  ViewManager* embedded = Embed(window_manager(), view);
389  EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(),
390            nested->id());
391  EXPECT_TRUE(embedded->GetRoots().front()->children().empty());
392  EXPECT_TRUE(nested->parent() == NULL);
393}
394
395// http://crbug.com/396300
396TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) {
397  View* view = View::Create(window_manager());
398  window_manager()->GetRoots().front()->AddChild(view);
399  ViewManager* embedded = Embed(window_manager(), view);
400
401  Id view_id = view->id();
402
403  UnloadApplication(GURL(kWindowManagerURL));
404
405  std::set<Id> views;
406  views.insert(view_id);
407  WaitForDestruction(embedded, &views);
408
409  EXPECT_TRUE(embedded->GetRoots().empty());
410}
411
412// TODO(beng): write a replacement test for the one that once existed here:
413// This test validates the following scenario:
414// -  a view originating from one connection
415// -  a view originating from a second connection
416// +  the connection originating the view is destroyed
417// -> the view should still exist (since the second connection is live) but
418//    should be disconnected from any views.
419// http://crbug.com/396300
420//
421// TODO(beng): The new test should validate the scenario as described above
422//             except that the second connection still has a valid tree.
423
424// Verifies that bounds changes applied to a view hierarchy in one connection
425// are reflected to another.
426TEST_F(ViewManagerTest, SetBounds) {
427  View* view = View::Create(window_manager());
428  window_manager()->GetRoots().front()->AddChild(view);
429  ViewManager* embedded = Embed(window_manager(), view);
430
431  View* view_in_embedded = embedded->GetViewById(view->id());
432  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
433
434  view->SetBounds(gfx::Rect(100, 100));
435  EXPECT_NE(view->bounds(), view_in_embedded->bounds());
436  WaitForBoundsToChange(view_in_embedded);
437  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
438}
439
440// Verifies that bounds changes applied to a view owned by a different
441// connection are refused.
442TEST_F(ViewManagerTest, SetBoundsSecurity) {
443  View* view = View::Create(window_manager());
444  window_manager()->GetRoots().front()->AddChild(view);
445  ViewManager* embedded = Embed(window_manager(), view);
446
447  View* view_in_embedded = embedded->GetViewById(view->id());
448  view->SetBounds(gfx::Rect(800, 600));
449  WaitForBoundsToChange(view_in_embedded);
450
451  view_in_embedded->SetBounds(gfx::Rect(1024, 768));
452  // Bounds change should have been rejected.
453  EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
454}
455
456// Verifies that a view can only be destroyed by the connection that created it.
457TEST_F(ViewManagerTest, DestroySecurity) {
458  View* view = View::Create(window_manager());
459  window_manager()->GetRoots().front()->AddChild(view);
460  ViewManager* embedded = Embed(window_manager(), view);
461
462  View* view_in_embedded = embedded->GetViewById(view->id());
463
464  ViewTracker tracker2(view_in_embedded);
465  view_in_embedded->Destroy();
466  // View should not have been destroyed.
467  EXPECT_TRUE(tracker2.is_valid());
468
469  ViewTracker tracker1(view);
470  view->Destroy();
471  EXPECT_FALSE(tracker1.is_valid());
472}
473
474TEST_F(ViewManagerTest, MultiRoots) {
475  View* view1 = View::Create(window_manager());
476  window_manager()->GetRoots().front()->AddChild(view1);
477  View* view2 = View::Create(window_manager());
478  window_manager()->GetRoots().front()->AddChild(view2);
479  ViewManager* embedded1 = Embed(window_manager(), view1);
480  ViewManager* embedded2 = Embed(window_manager(), view2);
481  EXPECT_EQ(embedded1, embedded2);
482}
483
484TEST_F(ViewManagerTest, EmbeddingIdentity) {
485  View* view = View::Create(window_manager());
486  window_manager()->GetRoots().front()->AddChild(view);
487  ViewManager* embedded = Embed(window_manager(), view);
488  EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
489}
490
491TEST_F(ViewManagerTest, Reorder) {
492  View* view1 = View::Create(window_manager());
493  window_manager()->GetRoots().front()->AddChild(view1);
494
495  ViewManager* embedded = Embed(window_manager(), view1);
496
497  View* view11 = View::Create(embedded);
498  embedded->GetRoots().front()->AddChild(view11);
499  View* view12 = View::Create(embedded);
500  embedded->GetRoots().front()->AddChild(view12);
501
502  View* view1_in_wm = window_manager()->GetViewById(view1->id());
503
504  {
505    WaitForTreeSizeToMatch(view1, 2u);
506    view11->MoveToFront();
507    WaitForOrderChange(window_manager(),
508                       window_manager()->GetViewById(view11->id()));
509
510    EXPECT_EQ(view1_in_wm->children().front(),
511              window_manager()->GetViewById(view12->id()));
512    EXPECT_EQ(view1_in_wm->children().back(),
513              window_manager()->GetViewById(view11->id()));
514  }
515
516  {
517    view11->MoveToBack();
518    WaitForOrderChange(window_manager(),
519                       window_manager()->GetViewById(view11->id()));
520
521    EXPECT_EQ(view1_in_wm->children().front(),
522              window_manager()->GetViewById(view11->id()));
523    EXPECT_EQ(view1_in_wm->children().back(),
524              window_manager()->GetViewById(view12->id()));
525  }
526}
527
528// TODO(beng): tests for view event dispatcher.
529// - verify that we see events for all views.
530
531// TODO(beng): tests for focus:
532// - focus between two views known to a connection
533// - focus between views unknown to one of the connections.
534// - focus between views unknown to either connection.
535
536}  // namespace mojo
537