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 "base/bind.h"
6#include "base/memory/scoped_vector.h"
7#include "mojo/application_manager/application_manager.h"
8#include "mojo/public/cpp/application/application_delegate.h"
9#include "mojo/public/cpp/application/application_impl.h"
10#include "mojo/public/cpp/application/service_provider_impl.h"
11#include "mojo/public/interfaces/application/service_provider.mojom.h"
12#include "mojo/services/public/cpp/view_manager/types.h"
13#include "mojo/services/public/cpp/view_manager/view.h"
14#include "mojo/services/public/cpp/view_manager/view_manager.h"
15#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
16#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
17#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
18#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h"
19#include "mojo/shell/shell_test_helper.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace mojo {
23namespace {
24
25const char kTestServiceURL[] = "mojo:test_url";
26
27void EmptyResultCallback(bool result) {}
28
29// Callback from Embed(). |result| is the result of the Embed() call and
30// |run_loop| the nested RunLoop.
31void ResultCallback(bool* result_cache, base::RunLoop* run_loop, bool result) {
32  *result_cache = result;
33  run_loop->Quit();
34}
35
36// Responsible for establishing the initial ViewManagerService connection.
37// Blocks until result is determined.
38bool InitEmbed(ViewManagerInitService* view_manager_init,
39               const std::string& url) {
40  bool result = false;
41  base::RunLoop run_loop;
42  ServiceProviderPtr sp;
43  BindToProxy(new ServiceProviderImpl, &sp);
44  view_manager_init->Embed(url, sp.Pass(),
45                           base::Bind(&ResultCallback, &result, &run_loop));
46  run_loop.Run();
47  return result;
48}
49
50class TestWindowManagerClient : public WindowManagerClient {
51 public:
52  typedef base::Callback<void(Id, Id)>
53      TwoNodeCallback;
54
55  explicit TestWindowManagerClient(base::RunLoop* run_loop)
56      : run_loop_(run_loop) {}
57  virtual ~TestWindowManagerClient() {}
58
59  void set_focus_changed_callback(const TwoNodeCallback& callback) {
60    focus_changed_callback_ = callback;
61  }
62  void set_active_window_changed_callback(const TwoNodeCallback& callback) {
63    active_window_changed_callback_ = callback;
64  }
65
66 private:
67  // Overridden from WindowManagerClient:
68  virtual void OnWindowManagerReady() MOJO_OVERRIDE {
69    run_loop_->Quit();
70  }
71  virtual void OnCaptureChanged(
72      Id old_capture_node_id,
73      Id new_capture_node_id) MOJO_OVERRIDE {
74  }
75  virtual void OnFocusChanged(
76      Id old_focused_node_id,
77      Id new_focused_node_id) MOJO_OVERRIDE {
78    if (!focus_changed_callback_.is_null())
79      focus_changed_callback_.Run(old_focused_node_id, new_focused_node_id);
80  }
81  virtual void OnActiveWindowChanged(
82      Id old_active_window,
83      Id new_active_window) MOJO_OVERRIDE {
84    if (!active_window_changed_callback_.is_null())
85      active_window_changed_callback_.Run(old_active_window, new_active_window);
86  }
87
88  base::RunLoop* run_loop_;
89  TwoNodeCallback focus_changed_callback_;
90  TwoNodeCallback active_window_changed_callback_;
91
92  DISALLOW_COPY_AND_ASSIGN(TestWindowManagerClient);
93};
94
95class TestApplicationLoader : public ApplicationLoader,
96                              public ApplicationDelegate,
97                              public ViewManagerDelegate {
98 public:
99  typedef base::Callback<void(View*)> RootAddedCallback;
100
101  explicit TestApplicationLoader(const RootAddedCallback& root_added_callback)
102      : root_added_callback_(root_added_callback) {}
103  virtual ~TestApplicationLoader() {}
104
105 private:
106  // Overridden from ApplicationLoader:
107  virtual void Load(ApplicationManager* application_manager,
108                    const GURL& url,
109                    scoped_refptr<LoadCallbacks> callbacks) MOJO_OVERRIDE {
110    ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
111    if (!shell_handle.is_valid())
112      return;
113    scoped_ptr<ApplicationImpl> app(
114        new ApplicationImpl(this, shell_handle.Pass()));
115    apps_.push_back(app.release());
116  }
117  virtual void OnApplicationError(ApplicationManager* application_manager,
118                                  const GURL& url) MOJO_OVERRIDE {}
119
120  // Overridden from ApplicationDelegate:
121  virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
122    view_manager_client_factory_.reset(
123        new ViewManagerClientFactory(app->shell(), this));
124  }
125
126  virtual bool ConfigureIncomingConnection(
127      ApplicationConnection* connection) MOJO_OVERRIDE {
128    connection->AddService(view_manager_client_factory_.get());
129    return true;
130  }
131
132  // Overridden from ViewManagerDelegate:
133  virtual void OnEmbed(
134      ViewManager* view_manager,
135      View* root,
136      ServiceProviderImpl* exported_services,
137      scoped_ptr<ServiceProvider> imported_services) MOJO_OVERRIDE {
138    root_added_callback_.Run(root);
139  }
140  virtual void OnViewManagerDisconnected(
141      ViewManager* view_manager) MOJO_OVERRIDE {
142  }
143
144  RootAddedCallback root_added_callback_;
145
146  ScopedVector<ApplicationImpl> apps_;
147  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
148
149  DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
150};
151
152}  // namespace
153
154class WindowManagerApiTest : public testing::Test {
155 public:
156  WindowManagerApiTest() {}
157  virtual ~WindowManagerApiTest() {}
158
159 protected:
160  typedef std::pair<Id, Id> TwoIds;
161
162  Id WaitForEmbed() {
163    Id id;
164    base::RunLoop run_loop;
165    root_added_callback_ = base::Bind(&WindowManagerApiTest::OnEmbed,
166                                      base::Unretained(this), &id, &run_loop);
167    run_loop.Run();
168    return id;
169  }
170
171  TwoIds WaitForFocusChange() {
172    TwoIds old_and_new;
173    base::RunLoop run_loop;
174    window_manager_client()->set_focus_changed_callback(
175        base::Bind(&WindowManagerApiTest::OnFocusChanged,
176                   base::Unretained(this), &old_and_new, &run_loop));
177    run_loop.Run();
178    return old_and_new;
179  }
180
181  TwoIds WaitForActiveWindowChange() {
182    TwoIds old_and_new;
183    base::RunLoop run_loop;
184    window_manager_client()->set_active_window_changed_callback(
185        base::Bind(&WindowManagerApiTest::OnActiveWindowChanged,
186                   base::Unretained(this), &old_and_new, &run_loop));
187    run_loop.Run();
188    return old_and_new;
189  }
190
191  Id OpenWindow() {
192    return OpenWindowWithURL(kTestServiceURL);
193  }
194
195  Id OpenWindowWithURL(const std::string& url) {
196    InitEmbed(view_manager_init_.get(), url);
197    return WaitForEmbed();
198  }
199
200  TestWindowManagerClient* window_manager_client() {
201    return window_manager_client_.get();
202  }
203
204  WindowManagerServicePtr window_manager_;
205
206 private:
207  // Overridden from testing::Test:
208  virtual void SetUp() MOJO_OVERRIDE {
209    test_helper_.Init();
210    test_helper_.SetLoaderForURL(
211        scoped_ptr<ApplicationLoader>(new TestApplicationLoader(base::Bind(
212            &WindowManagerApiTest::OnRootAdded, base::Unretained(this)))),
213        GURL(kTestServiceURL));
214    test_helper_.application_manager()->ConnectToService(
215        GURL("mojo:mojo_view_manager"), &view_manager_init_);
216    ASSERT_TRUE(InitEmbed(view_manager_init_.get(),
217                          "mojo:mojo_core_window_manager"));
218    ConnectToWindowManager();
219  }
220  virtual void TearDown() MOJO_OVERRIDE {}
221
222  void ConnectToWindowManager() {
223    test_helper_.application_manager()->ConnectToService(
224        GURL("mojo:mojo_core_window_manager"), &window_manager_);
225    base::RunLoop connect_loop;
226    window_manager_client_.reset(new TestWindowManagerClient(&connect_loop));
227    window_manager_.set_client(window_manager_client());
228    connect_loop.Run();
229  }
230
231  void OnRootAdded(View* root) {
232    if (!root_added_callback_.is_null())
233      root_added_callback_.Run(root);
234  }
235
236  void OnEmbed(Id* root_id,
237               base::RunLoop* loop,
238               View* root) {
239    *root_id = root->id();
240    loop->Quit();
241  }
242
243  void OnFocusChanged(TwoIds* old_and_new,
244                      base::RunLoop* run_loop,
245                      Id old_focused_node_id,
246                      Id new_focused_node_id) {
247    DCHECK(old_and_new);
248    old_and_new->first = old_focused_node_id;
249    old_and_new->second = new_focused_node_id;
250    run_loop->Quit();
251  }
252
253  void OnActiveWindowChanged(TwoIds* old_and_new,
254                             base::RunLoop* run_loop,
255                             Id old_focused_node_id,
256                             Id new_focused_node_id) {
257    DCHECK(old_and_new);
258    old_and_new->first = old_focused_node_id;
259    old_and_new->second = new_focused_node_id;
260    run_loop->Quit();
261  }
262
263  shell::ShellTestHelper test_helper_;
264  ViewManagerInitServicePtr view_manager_init_;
265  scoped_ptr<TestWindowManagerClient> window_manager_client_;
266  TestApplicationLoader::RootAddedCallback root_added_callback_;
267
268  DISALLOW_COPY_AND_ASSIGN(WindowManagerApiTest);
269};
270
271TEST_F(WindowManagerApiTest, FocusAndActivateWindow) {
272  Id first_window = OpenWindow();
273  window_manager_->FocusWindow(first_window,
274                               base::Bind(&EmptyResultCallback));
275  TwoIds ids = WaitForFocusChange();
276  EXPECT_TRUE(ids.first == 0);
277  EXPECT_EQ(ids.second, first_window);
278
279  Id second_window = OpenWindow();
280  window_manager_->ActivateWindow(second_window,
281                                  base::Bind(&EmptyResultCallback));
282  ids = WaitForActiveWindowChange();
283  EXPECT_EQ(ids.first, first_window);
284  EXPECT_EQ(ids.second, second_window);
285}
286
287}  // namespace mojo
288