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.
8 #include "base/command_line.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/apps/app_browsertest_util.h"
11 #include "chrome/browser/ui/app_list/app_list_service.h"
12 #include "chrome/browser/ui/app_list/app_list_service_views.h"
13 #include "chrome/browser/ui/app_list/app_list_shower_views.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "extensions/common/extension.h"
17 #include "extensions/common/switches.h"
18 #include "extensions/test/extension_test_message_listener.h"
19 #include "ui/app_list/app_list_switches.h"
20 #include "ui/app_list/views/app_list_main_view.h"
21 #include "ui/app_list/views/app_list_view.h"
22 #include "ui/app_list/views/contents_view.h"
23 #include "ui/app_list/views/search_box_view.h"
24 #include "ui/events/test/event_generator.h"
25 #include "ui/views/controls/textfield/textfield.h"
26 #include "ui/views/controls/webview/webview.h"
27 #include "ui/views/focus/focus_manager.h"
31 // The path of the test application within the "platform_apps" directory.
32 const char kCustomLauncherPagePath
[] = "custom_launcher_page";
34 // The app ID of the test application.
35 const char kCustomLauncherPageID
[] = "lmadimbbgapmngbiclpjjngmdickadpl";
39 // Browser tests for custom launcher pages, platform apps that run as a page in
40 // the app launcher. Within this test class, LoadAndLaunchPlatformApp runs the
41 // app inside the launcher, not as a standalone background page.
43 class CustomLauncherPageBrowserTest
44 : public extensions::PlatformAppBrowserTest
{
46 CustomLauncherPageBrowserTest() {}
48 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
49 PlatformAppBrowserTest::SetUpCommandLine(command_line
);
51 // Custom launcher pages only work in the experimental app list.
52 command_line
->AppendSwitch(app_list::switches::kEnableExperimentalAppList
);
54 // Ensure the app list does not close during the test.
55 command_line
->AppendSwitch(
56 app_list::switches::kDisableAppListDismissOnBlur
);
58 // The test app must be whitelisted to use launcher_page.
59 command_line
->AppendSwitchASCII(
60 extensions::switches::kWhitelistedExtensionID
, kCustomLauncherPageID
);
63 // Open the launcher. Ignores the Extension argument (this will simply
64 // activate any loaded launcher pages).
65 void LaunchPlatformApp(const extensions::Extension
* /*unused*/) override
{
66 AppListService
* service
=
67 AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE
);
69 service
->ShowForProfile(browser()->profile());
72 app_list::AppListView
* GetAppListView() {
73 app_list::AppListView
* app_list_view
= nullptr;
74 #if defined(OS_CHROMEOS)
75 ash::Shell
* shell
= ash::Shell::GetInstance();
76 app_list_view
= shell
->GetAppListView();
77 EXPECT_TRUE(shell
->GetAppListTargetVisibility());
79 AppListServiceViews
* service
= static_cast<AppListServiceViews
*>(
80 AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE
));
81 // The app list should have loaded instantly since the profile is already
83 EXPECT_TRUE(service
->IsAppListVisible());
84 app_list_view
= service
->shower().app_list();
89 // Set the active page on the app list, according to |state|. Does not wait
90 // for any animation or custom page to complete.
91 void SetActiveStateAndVerify(app_list::AppListModel::State state
) {
92 app_list::ContentsView
* contents_view
=
93 GetAppListView()->app_list_main_view()->contents_view();
94 contents_view
->SetActiveState(state
);
95 EXPECT_TRUE(contents_view
->IsStateActive(state
));
98 void SetCustomLauncherPageEnabled(bool enabled
) {
99 const base::string16 kLauncherPageDisableScript
=
100 base::ASCIIToUTF16("disableCustomLauncherPage();");
101 const base::string16 kLauncherPageEnableScript
=
102 base::ASCIIToUTF16("enableCustomLauncherPage();");
104 app_list::ContentsView
* contents_view
=
105 GetAppListView()->app_list_main_view()->contents_view();
106 views::WebView
* custom_page_view
=
107 static_cast<views::WebView
*>(contents_view
->custom_page_view());
108 content::RenderFrameHost
* custom_page_frame
=
109 custom_page_view
->GetWebContents()->GetMainFrame();
111 const char* test_message
=
112 enabled
? "launcherPageEnabled" : "launcherPageDisabled";
114 ExtensionTestMessageListener
listener(test_message
, false);
115 custom_page_frame
->ExecuteJavaScript(enabled
? kLauncherPageEnableScript
116 : kLauncherPageDisableScript
);
117 listener
.WaitUntilSatisfied();
121 DISALLOW_COPY_AND_ASSIGN(CustomLauncherPageBrowserTest
);
124 IN_PROC_BROWSER_TEST_F(CustomLauncherPageBrowserTest
,
125 OpenLauncherAndSwitchToCustomPage
) {
126 LoadAndLaunchPlatformApp(kCustomLauncherPagePath
, "Launched");
127 app_list::AppListView
* app_list_view
= GetAppListView();
128 app_list::ContentsView
* contents_view
=
129 app_list_view
->app_list_main_view()->contents_view();
132 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
135 ExtensionTestMessageListener
listener("onPageProgressAt1", false);
136 contents_view
->SetActiveState(
137 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
);
139 listener
.WaitUntilSatisfied();
142 ExtensionTestMessageListener
listener("onPageProgressAt0", false);
143 contents_view
->SetActiveState(app_list::AppListModel::STATE_START
);
145 listener
.WaitUntilSatisfied();
149 // Test that the app list will switch to the custom launcher page by sending a
150 // click inside the clickzone, or a mouse scroll event.
151 IN_PROC_BROWSER_TEST_F(CustomLauncherPageBrowserTest
,
152 EventsActivateSwitchToCustomPage
) {
153 LoadAndLaunchPlatformApp(kCustomLauncherPagePath
, "Launched");
154 // Use an event generator to ensure targeting is correct.
155 app_list::AppListView
* app_list_view
= GetAppListView();
156 app_list::ContentsView
* contents_view
=
157 app_list_view
->app_list_main_view()->contents_view();
158 gfx::NativeWindow window
= app_list_view
->GetWidget()->GetNativeWindow();
159 ui::test::EventGenerator
event_generator(window
->GetRootWindow(), window
);
161 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
163 // Find the clickzone.
164 gfx::Rect bounds
= contents_view
->GetCustomPageCollapsedBounds();
165 bounds
.Intersect(contents_view
->bounds());
166 gfx::Point point_in_clickzone
= bounds
.CenterPoint();
167 views::View::ConvertPointToWidget(contents_view
, &point_in_clickzone
);
169 // First try clicking 10px above the clickzone.
170 gfx::Point point_above_clickzone
= point_in_clickzone
;
171 point_above_clickzone
.set_y(bounds
.y() - 10);
172 views::View::ConvertPointToWidget(contents_view
, &point_above_clickzone
);
174 event_generator
.MoveMouseRelativeTo(window
, point_above_clickzone
);
175 event_generator
.ClickLeftButton();
177 // Should stay on the start page.
179 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
181 // Now click in the clickzone.
182 event_generator
.MoveMouseRelativeTo(window
, point_in_clickzone
);
183 // First, try disabling the custom page view. Click should do nothing.
184 SetCustomLauncherPageEnabled(false);
185 event_generator
.ClickLeftButton();
187 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
188 // Click again with it enabled. The active state should update immediately.
189 SetCustomLauncherPageEnabled(true);
190 event_generator
.ClickLeftButton();
191 EXPECT_TRUE(contents_view
->IsStateActive(
192 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
194 // Back to the start page. And send a mouse wheel event.
195 SetActiveStateAndVerify(app_list::AppListModel::STATE_START
);
196 // Generate wheel events above the clickzone.
197 event_generator
.MoveMouseRelativeTo(window
, point_above_clickzone
);
198 // Scrolling left, right or up should do nothing.
199 event_generator
.MoveMouseWheel(-5, 0);
200 event_generator
.MoveMouseWheel(5, 0);
201 event_generator
.MoveMouseWheel(0, 5);
203 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
204 // Scroll down to open launcher page.
205 event_generator
.MoveMouseWheel(0, -5);
206 EXPECT_TRUE(contents_view
->IsStateActive(
207 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
209 // Constants for gesture/trackpad events.
210 const base::TimeDelta step_delay
= base::TimeDelta::FromMilliseconds(300);
211 const int num_steps
= 5;
212 const int num_fingers
= 2;
214 #if defined(OS_CHROMEOS)
215 // Gesture events need to be in host coordinates. On Desktop platforms, the
216 // Widget is the host, so nothing needs to be done. On ChromeOS, the points
217 // need to be put into screen coordinates. This works because the root window
218 // assumes it fills the screen.
219 point_in_clickzone
= bounds
.CenterPoint();
220 point_above_clickzone
.SetPoint(point_in_clickzone
.x(), bounds
.y() - 10);
221 views::View::ConvertPointToScreen(contents_view
, &point_above_clickzone
);
222 views::View::ConvertPointToScreen(contents_view
, &point_in_clickzone
);
225 // Back to the start page. And send a scroll gesture.
226 SetActiveStateAndVerify(app_list::AppListModel::STATE_START
);
227 // Going down should do nothing.
228 event_generator
.GestureScrollSequence(
229 point_above_clickzone
, point_in_clickzone
, step_delay
, num_steps
);
231 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
232 // Now go up - should change state.
233 event_generator
.GestureScrollSequence(
234 point_in_clickzone
, point_above_clickzone
, step_delay
, num_steps
);
235 EXPECT_TRUE(contents_view
->IsStateActive(
236 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
238 // Back to the start page. And send a trackpad scroll event.
239 SetActiveStateAndVerify(app_list::AppListModel::STATE_START
);
240 // Going down left, right or up should do nothing.
241 event_generator
.ScrollSequence(point_in_clickzone
, step_delay
, -5, 0,
242 num_steps
, num_fingers
);
243 event_generator
.ScrollSequence(point_in_clickzone
, step_delay
, 5, 0,
244 num_steps
, num_fingers
);
245 event_generator
.ScrollSequence(point_in_clickzone
, step_delay
, 0, 5,
246 num_steps
, num_fingers
);
248 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
249 // Scroll up to open launcher page.
250 event_generator
.ScrollSequence(point_in_clickzone
, step_delay
, 0, -5,
251 num_steps
, num_fingers
);
252 EXPECT_TRUE(contents_view
->IsStateActive(
253 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
255 // Back to the start page. And send a tap gesture.
256 SetActiveStateAndVerify(app_list::AppListModel::STATE_START
);
257 // Tapping outside the clickzone should do nothing.
258 event_generator
.GestureTapAt(point_above_clickzone
);
260 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
261 // Now tap in the clickzone.
262 event_generator
.GestureTapAt(point_in_clickzone
);
263 EXPECT_TRUE(contents_view
->IsStateActive(
264 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
267 IN_PROC_BROWSER_TEST_F(CustomLauncherPageBrowserTest
, LauncherPageSubpages
) {
268 LoadAndLaunchPlatformApp(kCustomLauncherPagePath
, "Launched");
270 app_list::AppListView
* app_list_view
= GetAppListView();
271 app_list::AppListModel
* model
= app_list_view
->app_list_main_view()->model();
272 app_list::ContentsView
* contents_view
=
273 app_list_view
->app_list_main_view()->contents_view();
276 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
279 ExtensionTestMessageListener
listener("onPageProgressAt1", false);
280 contents_view
->SetActiveState(
281 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
);
282 listener
.WaitUntilSatisfied();
283 EXPECT_TRUE(contents_view
->IsStateActive(
284 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
285 // The app pushes 2 subpages when the launcher page is shown.
286 EXPECT_EQ(2, model
->custom_launcher_page_subpage_depth());
291 ExtensionTestMessageListener
listener("onPopSubpage", false);
292 EXPECT_TRUE(contents_view
->Back());
293 listener
.WaitUntilSatisfied();
294 EXPECT_TRUE(contents_view
->IsStateActive(
295 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
296 EXPECT_EQ(1, model
->custom_launcher_page_subpage_depth());
299 ExtensionTestMessageListener
listener("onPopSubpage", false);
300 EXPECT_TRUE(contents_view
->Back());
301 listener
.WaitUntilSatisfied();
302 EXPECT_TRUE(contents_view
->IsStateActive(
303 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
304 EXPECT_EQ(0, model
->custom_launcher_page_subpage_depth());
307 // Once all subpages are popped, the start page should show.
308 EXPECT_TRUE(contents_view
->Back());
310 // Immediately finish the animation.
311 contents_view
->Layout();
313 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
316 IN_PROC_BROWSER_TEST_F(CustomLauncherPageBrowserTest
,
318 const base::string16 kLauncherPageShowScript
=
319 base::ASCIIToUTF16("chrome.launcherPage.show();");
321 LoadAndLaunchPlatformApp(kCustomLauncherPagePath
, "Launched");
322 app_list::AppListView
* app_list_view
= GetAppListView();
323 app_list::ContentsView
* contents_view
=
324 app_list_view
->app_list_main_view()->contents_view();
326 views::WebView
* custom_page_view
=
327 static_cast<views::WebView
*>(contents_view
->custom_page_view());
329 content::RenderFrameHost
* custom_page_frame
=
330 custom_page_view
->GetWebContents()->GetMainFrame();
333 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
335 // Ensure launcherPage.show() will switch the page to the custom launcher page
336 // if the app launcher is already showing.
338 ExtensionTestMessageListener
listener("onPageProgressAt1", false);
339 custom_page_frame
->ExecuteJavaScript(kLauncherPageShowScript
);
341 listener
.WaitUntilSatisfied();
342 EXPECT_TRUE(contents_view
->IsStateActive(
343 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
346 // Ensure launcherPage.show() will show the app list if it's hidden.
348 // Close the app list immediately.
349 app_list_view
->Close();
350 app_list_view
->GetWidget()->Close();
352 ExtensionTestMessageListener
listener("onPageProgressAt1", false);
353 custom_page_frame
->ExecuteJavaScript(kLauncherPageShowScript
);
355 listener
.WaitUntilSatisfied();
357 // The app list view will have changed on ChromeOS.
358 app_list_view
= GetAppListView();
359 contents_view
= app_list_view
->app_list_main_view()->contents_view();
360 EXPECT_TRUE(contents_view
->IsStateActive(
361 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
365 IN_PROC_BROWSER_TEST_F(CustomLauncherPageBrowserTest
, LauncherPageSetEnabled
) {
366 LoadAndLaunchPlatformApp(kCustomLauncherPagePath
, "Launched");
367 app_list::AppListView
* app_list_view
= GetAppListView();
368 app_list::AppListModel
* model
= app_list_view
->app_list_main_view()->model();
369 app_list::ContentsView
* contents_view
=
370 app_list_view
->app_list_main_view()->contents_view();
372 views::WebView
* custom_page_view
=
373 static_cast<views::WebView
*>(contents_view
->custom_page_view());
375 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
377 EXPECT_TRUE(model
->custom_launcher_page_enabled());
378 EXPECT_TRUE(custom_page_view
->visible());
380 SetCustomLauncherPageEnabled(false);
381 EXPECT_FALSE(model
->custom_launcher_page_enabled());
382 EXPECT_FALSE(custom_page_view
->visible());
384 SetCustomLauncherPageEnabled(true);
385 EXPECT_TRUE(model
->custom_launcher_page_enabled());
386 EXPECT_TRUE(custom_page_view
->visible());
389 // Currently this is flaky.
390 // Disabled test http://crbug.com/463456
391 IN_PROC_BROWSER_TEST_F(CustomLauncherPageBrowserTest
,
392 DISABLED_LauncherPageFocusTraversal
) {
393 LoadAndLaunchPlatformApp(kCustomLauncherPagePath
, "Launched");
394 app_list::AppListView
* app_list_view
= GetAppListView();
395 app_list::ContentsView
* contents_view
=
396 app_list_view
->app_list_main_view()->contents_view();
397 app_list::SearchBoxView
* search_box_view
=
398 app_list_view
->app_list_main_view()->search_box_view();
401 contents_view
->IsStateActive(app_list::AppListModel::STATE_START
));
402 EXPECT_EQ(app_list_view
->GetFocusManager()->GetFocusedView(),
403 search_box_view
->search_box());
406 ExtensionTestMessageListener
listener("onPageProgressAt1", false);
407 contents_view
->SetActiveState(
408 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
);
409 listener
.WaitUntilSatisfied();
410 EXPECT_TRUE(contents_view
->IsStateActive(
411 app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
));
412 EXPECT_EQ(app_list_view
->GetFocusManager()->GetFocusedView(),
413 search_box_view
->search_box());
416 ExtensionTestMessageListener
listener("textfieldFocused", false);
417 app_list_view
->GetFocusManager()->AdvanceFocus(false);
418 listener
.WaitUntilSatisfied();
419 EXPECT_NE(app_list_view
->GetFocusManager()->GetFocusedView(),
420 search_box_view
->search_box());
423 ExtensionTestMessageListener
listener("textfieldBlurred", false);
424 app_list_view
->GetFocusManager()->AdvanceFocus(false);
425 listener
.WaitUntilSatisfied();
426 EXPECT_EQ(app_list_view
->GetFocusManager()->GetFocusedView(),
427 search_box_view
->search_box());