1// Copyright 2013 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 "chrome/browser/apps/app_browsertest_util.h"
6#include "chrome/browser/profiles/profile.h"
7#include "chrome/browser/ui/browser.h"
8#include "chrome/browser/ui/extensions/application_launch.h"
9#include "content/public/browser/notification_service.h"
10#include "content/public/test/test_utils.h"
11#include "extensions/browser/app_window/app_window_geometry_cache.h"
12#include "extensions/common/constants.h"
13#include "extensions/common/extension.h"
14#include "extensions/test/extension_test_message_listener.h"
15#include "extensions/test/result_catcher.h"
16
17using extensions::AppWindowGeometryCache;
18using extensions::ResultCatcher;
19
20// This helper class can be used to wait for changes in the app window
21// geometry cache registry for a specific window in a specific extension.
22class GeometryCacheChangeHelper : AppWindowGeometryCache::Observer {
23 public:
24  GeometryCacheChangeHelper(AppWindowGeometryCache* cache,
25                            const std::string& extension_id,
26                            const std::string& window_id,
27                            const gfx::Rect& bounds)
28      : cache_(cache),
29        extension_id_(extension_id),
30        window_id_(window_id),
31        bounds_(bounds),
32        satisfied_(false),
33        waiting_(false) {
34    cache_->AddObserver(this);
35  }
36
37  // This method will block until the app window geometry cache registry will
38  // provide a bound for |window_id_| that is entirely different (as in x/y/w/h)
39  // from the initial |bounds_|.
40  void WaitForEntirelyChanged() {
41    if (satisfied_)
42      return;
43
44    waiting_ = true;
45    content::RunMessageLoop();
46  }
47
48  // Implements the content::NotificationObserver interface.
49  virtual void OnGeometryCacheChanged(const std::string& extension_id,
50                                      const std::string& window_id,
51                                      const gfx::Rect& bounds)
52      OVERRIDE {
53    if (extension_id != extension_id_ || window_id != window_id_)
54      return;
55
56    if (bounds_.x() != bounds.x() &&
57        bounds_.y() != bounds.y() &&
58        bounds_.width() != bounds.width() &&
59        bounds_.height() != bounds.height()) {
60      satisfied_ = true;
61      cache_->RemoveObserver(this);
62
63      if (waiting_)
64        base::MessageLoopForUI::current()->Quit();
65    }
66  }
67
68 private:
69  AppWindowGeometryCache* cache_;
70  std::string extension_id_;
71  std::string window_id_;
72  gfx::Rect bounds_;
73  bool satisfied_;
74  bool waiting_;
75};
76
77// Helper class for tests related to the Apps Window API (chrome.app.window).
78class AppWindowAPITest : public extensions::PlatformAppBrowserTest {
79 protected:
80  bool RunAppWindowAPITest(const char* testName) {
81    if (!BeginAppWindowAPITest(testName))
82      return false;
83
84    ResultCatcher catcher;
85    if (!catcher.GetNextResult()) {
86      message_ = catcher.message();
87      return false;
88    }
89
90    return true;
91  }
92
93  bool RunAppWindowAPITestAndWaitForRoundTrip(const char* testName) {
94    if (!BeginAppWindowAPITest(testName))
95      return false;
96
97    ExtensionTestMessageListener round_trip_listener("WaitForRoundTrip", true);
98    if (!round_trip_listener.WaitUntilSatisfied()) {
99      message_ = "Did not get the 'WaitForRoundTrip' message.";
100      return false;
101    }
102
103    round_trip_listener.Reply("");
104
105    ResultCatcher catcher;
106    if (!catcher.GetNextResult()) {
107      message_ = catcher.message();
108      return false;
109    }
110
111    return true;
112  }
113
114 private:
115  bool BeginAppWindowAPITest(const char* testName) {
116    ExtensionTestMessageListener launched_listener("Launched", true);
117    LoadAndLaunchPlatformApp("window_api", &launched_listener);
118    if (!launched_listener.WaitUntilSatisfied()) {
119      message_ = "Did not get the 'Launched' message.";
120      return false;
121    }
122
123    launched_listener.Reply(testName);
124    return true;
125  }
126};
127
128// These tests are flaky after https://codereview.chromium.org/57433010/.
129// See http://crbug.com/319613.
130
131IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestCreate) {
132  ASSERT_TRUE(RunAppWindowAPITest("testCreate")) << message_;
133}
134
135IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestSingleton) {
136  ASSERT_TRUE(RunAppWindowAPITest("testSingleton")) << message_;
137}
138
139IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestCloseEvent) {
140  ASSERT_TRUE(RunAppWindowAPITest("testCloseEvent")) << message_;
141}
142
143IN_PROC_BROWSER_TEST_F(AppWindowAPITest, DISABLED_TestMaximize) {
144  ASSERT_TRUE(RunAppWindowAPITest("testMaximize")) << message_;
145}
146
147IN_PROC_BROWSER_TEST_F(AppWindowAPITest, DISABLED_TestRestore) {
148  ASSERT_TRUE(RunAppWindowAPITest("testRestore")) << message_;
149}
150
151IN_PROC_BROWSER_TEST_F(AppWindowAPITest, DISABLED_TestRestoreAfterClose) {
152  ASSERT_TRUE(RunAppWindowAPITest("testRestoreAfterClose")) << message_;
153}
154
155// These tests will be flaky in Linux as window bounds change asynchronously.
156#if defined(OS_LINUX)
157#define MAYBE_TestDeprecatedBounds DISABLED_TestDeprecatedBounds
158#define MAYBE_TestInitialBounds DISABLED_TestInitialBounds
159#define MAYBE_TestInitialConstraints DISABLED_TestInitialConstraints
160#define MAYBE_TestSetBounds DISABLED_TestSetBounds
161#define MAYBE_TestSetSizeConstraints DISABLED_TestSetSizeConstraints
162#else
163#define MAYBE_TestDeprecatedBounds TestDeprecatedBounds
164#define MAYBE_TestInitialBounds TestInitialBounds
165#define MAYBE_TestInitialConstraints TestInitialConstraints
166#define MAYBE_TestSetBounds TestSetBounds
167#define MAYBE_TestSetSizeConstraints TestSetSizeConstraints
168#endif
169
170IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestDeprecatedBounds) {
171  ASSERT_TRUE(RunAppWindowAPITest("testDeprecatedBounds")) << message_;
172}
173
174IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestInitialBounds) {
175  ASSERT_TRUE(RunAppWindowAPITest("testInitialBounds")) << message_;
176}
177
178IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestInitialConstraints) {
179  ASSERT_TRUE(RunAppWindowAPITest("testInitialConstraints")) << message_;
180}
181
182IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestSetBounds) {
183  ASSERT_TRUE(RunAppWindowAPITest("testSetBounds")) << message_;
184}
185
186IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestSetSizeConstraints) {
187  ASSERT_TRUE(RunAppWindowAPITest("testSetSizeConstraints")) << message_;
188}
189
190// Flaky failures on mac_rel and WinXP, see http://crbug.com/324915.
191IN_PROC_BROWSER_TEST_F(AppWindowAPITest,
192                       DISABLED_TestRestoreGeometryCacheChange) {
193  // This test is similar to the other AppWindowAPI tests except that at some
194  // point the app will send a 'ListenGeometryChange' message at which point the
195  // test will check if the geometry cache entry for the test window has
196  // changed. When the change happens, the test will let the app know so it can
197  // continue running.
198  ExtensionTestMessageListener launched_listener("Launched", true);
199
200  content::WindowedNotificationObserver app_loaded_observer(
201      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
202      content::NotificationService::AllSources());
203
204  const extensions::Extension* extension = LoadExtension(
205      test_data_dir_.AppendASCII("platform_apps").AppendASCII("window_api"));
206  EXPECT_TRUE(extension);
207
208  OpenApplication(AppLaunchParams(browser()->profile(),
209                                  extension,
210                                  extensions::LAUNCH_CONTAINER_NONE,
211                                  NEW_WINDOW));
212
213  ExtensionTestMessageListener geometry_listener("ListenGeometryChange", true);
214
215  ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
216  launched_listener.Reply("testRestoreAfterGeometryCacheChange");
217
218  ASSERT_TRUE(geometry_listener.WaitUntilSatisfied());
219
220  GeometryCacheChangeHelper geo_change_helper_1(
221      AppWindowGeometryCache::Get(browser()->profile()),
222      extension->id(),
223      // The next line has information that has to stay in sync with the app.
224      "test-ra",
225      gfx::Rect(200, 200, 200, 200));
226
227  GeometryCacheChangeHelper geo_change_helper_2(
228      AppWindowGeometryCache::Get(browser()->profile()),
229      extension->id(),
230      // The next line has information that has to stay in sync with the app.
231      "test-rb",
232      gfx::Rect(200, 200, 200, 200));
233
234  // These calls will block until the app window geometry cache will change.
235  geo_change_helper_1.WaitForEntirelyChanged();
236  geo_change_helper_2.WaitForEntirelyChanged();
237
238  ResultCatcher catcher;
239  geometry_listener.Reply("");
240  ASSERT_TRUE(catcher.GetNextResult());
241}
242
243IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestBadging) {
244  ASSERT_TRUE(
245      RunAppWindowAPITestAndWaitForRoundTrip("testBadging")) << message_;
246}
247
248// TODO(benwells): Implement on Mac.
249#if defined(USE_AURA)
250IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColors) {
251  ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_;
252}
253
254// TODO(benwells): Remove this test once all the things are merged together. It
255// is currently present as this feature was previously disabled on stable
256// channel, so the test is to ensure it has all been re-enabled properly.
257IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColorsInStable) {
258  extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_STABLE);
259  ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_;
260}
261#endif
262
263IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestVisibleOnAllWorkspaces) {
264  ASSERT_TRUE(
265      RunAppWindowAPITestAndWaitForRoundTrip("testVisibleOnAllWorkspaces"))
266      << message_;
267}
268