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