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.
6 #include "base/bind_helpers.h"
7 #include "base/macros.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/time/time.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/test/base/interactive_test_utils.h"
14 #include "chrome/test/ppapi/ppapi_test.h"
15 #include "content/public/browser/render_widget_host.h"
16 #include "content/public/browser/render_widget_host_view.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/test/test_utils.h"
19 #include "third_party/skia/include/core/SkColor.h"
23 #if defined(OS_MACOSX)
24 const bool kIsMacUI
= true;
26 const bool kIsMacUI
= false;
29 // Runs the current MessageLoop until |condition| is true or timeout.
30 bool RunLoopUntil(const base::Callback
<bool()>& condition
) {
31 const base::TimeTicks start_time
= base::TimeTicks::Now();
32 while (!condition
.Run()) {
33 const base::TimeTicks current_time
= base::TimeTicks::Now();
34 if (current_time
- start_time
> base::TimeDelta::FromSeconds(10)) {
35 ADD_FAILURE() << "Condition not met within ten seconds.";
39 base::MessageLoop::current()->PostDelayedTask(
41 base::MessageLoop::QuitClosure(),
42 base::TimeDelta::FromMilliseconds(20));
43 content::RunMessageLoop();
50 // A BrowserTest that opens a test page that launches a simulated fullscreen
51 // Flash plugin. The plugin responds to mouse clicks and key presses by
52 // changing color. Once launched, the browser UI can be tested to confirm the
53 // desired interactive behaviors.
54 class FlashFullscreenInteractiveBrowserTest
: public OutOfProcessPPAPITest
{
56 FlashFullscreenInteractiveBrowserTest() {}
57 ~FlashFullscreenInteractiveBrowserTest() override
{}
60 content::WebContents
* GetActiveWebContents() const {
61 return browser()->tab_strip_model()->GetActiveWebContents();
64 // A simple way to convince libcontent and the browser UI that a tab is being
65 // screen captured. During tab capture, Flash fullscreen remains embedded
66 // within the tab content area of a non-fullscreened browser window.
67 void StartFakingTabCapture() {
68 GetActiveWebContents()->IncrementCapturerCount(gfx::Size(360, 240));
71 bool LaunchFlashFullscreen() {
72 // This navigates to a page that runs the simulated fullscreen Flash
73 // plugin. It will block until the plugin has completed an attempt to enter
74 // Flash fullscreen mode.
75 OutOfProcessPPAPITest::RunTest("FlashFullscreenForBrowserUI");
77 if (::testing::Test::HasFailure()) {
78 ADD_FAILURE() << ("Failed to launch simulated fullscreen Flash plugin. "
79 "Interactive UI testing cannot proceed.");
83 EXPECT_TRUE(ObserveTabIsInFullscreen(true));
85 return !::testing::Test::HasFailure();
88 void UseAcceleratorToOpenNewTab() {
89 content::WebContents
* const old_tab_contents
= GetActiveWebContents();
90 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
91 browser(), ui::VKEY_T
, !kIsMacUI
, false, false, kIsMacUI
));
92 EXPECT_TRUE(RunLoopUntil(base::Bind(
93 &FlashFullscreenInteractiveBrowserTest::IsObservingActiveWebContents
,
94 base::Unretained(this),
99 void UseAcceleratorToSwitchToTab(int tab_index
) {
100 content::WebContents
* const old_tab_contents
= GetActiveWebContents();
101 const ui::KeyboardCode key_code
=
102 static_cast<ui::KeyboardCode
>(ui::VKEY_1
+ tab_index
);
103 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
104 browser(), key_code
, !kIsMacUI
, false, false, kIsMacUI
));
105 EXPECT_TRUE(RunLoopUntil(base::Bind(
106 &FlashFullscreenInteractiveBrowserTest::IsObservingActiveWebContents
,
107 base::Unretained(this),
113 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
114 browser(), ui::VKEY_ESCAPE
, false, false, false, false));
117 void PressSpacebar() {
118 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
119 browser(), ui::VKEY_SPACE
, false, false, false, false));
122 void SpamSpacebar() {
123 for (int i
= 0; i
< 11; ++i
)
127 void ClickOnTabContainer() {
128 ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER
);
131 void ClickOnOmnibox() {
132 ui_test_utils::ClickOnView(browser(), VIEW_ID_OMNIBOX
);
135 bool ObserveTabIsInFullscreen(bool expected_in_fullscreen
) const {
136 if (!RunLoopUntil(base::Bind(
137 &FlashFullscreenInteractiveBrowserTest::IsObservingTabInFullscreen
,
138 base::Unretained(this),
139 GetActiveWebContents(),
140 expected_in_fullscreen
)))
143 if (expected_in_fullscreen
) {
144 if (!GetActiveWebContents()->GetFullscreenRenderWidgetHostView()) {
146 << "WebContents should have a fullscreen RenderWidgetHostView.";
149 EXPECT_EQ(GetActiveWebContents()->GetCapturerCount() > 0,
151 ->exclusive_access_manager()
152 ->fullscreen_controller()
153 ->IsWindowFullscreenForTabOrPending());
159 bool ObserveFlashHasFocus(content::WebContents
* contents
,
160 bool expected_to_have_focus
) const {
161 if (!RunLoopUntil(base::Bind(
162 &FlashFullscreenInteractiveBrowserTest::IsObservingFlashHasFocus
,
163 base::Unretained(this),
165 expected_to_have_focus
)))
168 if (expected_to_have_focus
) {
169 content::RenderWidgetHostView
* const web_page_view
=
170 contents
->GetRenderWidgetHostView();
171 EXPECT_FALSE(web_page_view
&& web_page_view
->HasFocus())
172 << "Both RenderWidgetHostViews cannot have focus at the same time.";
174 if (contents
== GetActiveWebContents())
175 EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(),
176 VIEW_ID_TAB_CONTAINER
));
182 bool ObserveFlashFillColor(SkColor expected_color
) const {
183 return RunLoopUntil(base::Bind(
184 &FlashFullscreenInteractiveBrowserTest::IsObservingFlashFillColor
,
185 base::Unretained(this),
190 bool IsObservingTabInFullscreen(content::WebContents
* contents
,
191 bool expected_in_fullscreen
) const {
192 return expected_in_fullscreen
==
194 ->exclusive_access_manager()
195 ->fullscreen_controller()
196 ->IsFullscreenForTabOrPending(contents
);
199 bool IsObservingFlashHasFocus(content::WebContents
* contents
,
200 bool expected_to_have_focus
) const {
201 content::RenderWidgetHostView
* const flash_fs_view
=
202 contents
->GetFullscreenRenderWidgetHostView();
203 const bool flash_has_focus
= flash_fs_view
&& flash_fs_view
->HasFocus();
204 return flash_has_focus
== expected_to_have_focus
;
207 bool IsObservingActiveWebContents(content::WebContents
* contents
,
208 bool expected_active_contents
) const {
209 return (contents
== GetActiveWebContents()) == expected_active_contents
;
212 bool IsObservingFlashFillColor(SkColor expected_color
) const {
213 content::RenderWidgetHostView
* const flash_fs_view
=
214 GetActiveWebContents()->GetFullscreenRenderWidgetHostView();
215 content::RenderWidgetHost
* const flash_fs_host
=
216 flash_fs_view
? flash_fs_view
->GetRenderWidgetHost() : nullptr;
217 if (!flash_fs_host
) {
218 ADD_FAILURE() << "Flash fullscreen RenderWidgetHost is gone.";
222 // When a widget is first shown, it can take some time before it is ready
223 // for copying from its backing store. This is a transient condition, and
224 // so it is not being treated as a test failure.
225 if (!flash_fs_host
->CanCopyFromBackingStore())
228 // Copy and examine the upper-left pixel of the widget and compare it to the
230 bool is_expected_color
= false;
231 flash_fs_host
->CopyFromBackingStore(
232 gfx::Rect(0, 0, 1, 1),
235 &FlashFullscreenInteractiveBrowserTest::CheckBitmapForFillColor
,
238 base::MessageLoop::QuitClosure()),
240 content::RunMessageLoop();
242 return is_expected_color
;
245 static void CheckBitmapForFillColor(SkColor expected_color
,
246 bool* is_expected_color
,
247 const base::Closure
& done_cb
,
248 const SkBitmap
& bitmap
,
249 content::ReadbackResponse response
) {
250 if (response
== content::READBACK_SUCCESS
) {
251 SkAutoLockPixels
lock_pixels(bitmap
);
252 if (bitmap
.width() > 0 && bitmap
.height() > 0)
253 *is_expected_color
= (bitmap
.getColor(0, 0) == expected_color
);
258 DISALLOW_COPY_AND_ASSIGN(FlashFullscreenInteractiveBrowserTest
);
261 // Tests that launching and exiting fullscreen-within-tab works.
262 IN_PROC_BROWSER_TEST_F(FlashFullscreenInteractiveBrowserTest
,
263 FullscreenWithinTab_EscapeKeyExitsFullscreen
) {
264 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
265 StartFakingTabCapture();
266 ASSERT_TRUE(LaunchFlashFullscreen());
267 content::WebContents
* const first_tab_contents
= GetActiveWebContents();
268 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
270 EXPECT_TRUE(ObserveTabIsInFullscreen(false));
273 // This tests that browser UI focus behavior is correct when switching between
274 // tabs; particularly, that that focus between the omnibox and tab contents is
275 // stored/restored correctly. Mouse and keyboard events are used to confirm
276 // that the widget the UI thinks is focused is the one that responds to these
279 // Flaky, see http://crbug.com/444476
280 IN_PROC_BROWSER_TEST_F(FlashFullscreenInteractiveBrowserTest
,
281 DISABLED_FullscreenWithinTab_FocusWhenSwitchingTabs
) {
282 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
283 StartFakingTabCapture();
284 ASSERT_TRUE(LaunchFlashFullscreen());
286 // Upon entering fullscreen, the Flash widget should have focus and be filled
288 content::WebContents
* const first_tab_contents
= GetActiveWebContents();
289 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
290 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorGREEN
));
292 // Pressing the spacebar on the keyboard should change the fill color to red
293 // to indicate the plugin truly does have the keyboard focus. Clicking on the
294 // view should change the fill color to blue.
296 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED
));
297 ClickOnTabContainer();
298 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
300 // Launch a new tab. The Flash widget should have lost focus.
301 UseAcceleratorToOpenNewTab();
302 content::WebContents
* const second_tab_contents
= GetActiveWebContents();
303 ASSERT_NE(first_tab_contents
, second_tab_contents
);
304 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
306 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
309 // Switch back to first tab. The plugin should not have responded to the key
310 // presses above (while the omnibox was focused), and should regain focus only
311 // now. Poke it with key and mouse events to confirm.
312 UseAcceleratorToSwitchToTab(0);
313 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
314 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
316 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED
));
317 ClickOnTabContainer();
318 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
320 // Click on the omnibox while still in the first tab, and the Flash widget
321 // should lose focus. Key presses should not affect the color of the Flash
324 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
325 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
327 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
329 // Switch to the second tab, click on the web page content, and then go back
330 // to the first tab. Focus should have been restored to the omnibox when
331 // going back to the first tab, and so key presses should not change the color
332 // of the Flash widget.
333 UseAcceleratorToSwitchToTab(1);
334 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
335 ClickOnTabContainer();
336 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
337 UseAcceleratorToSwitchToTab(0);
338 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
339 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
341 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
343 // Clicking on the Flash widget should give it focus again.
344 ClickOnTabContainer();
345 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
346 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED
));
348 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
350 // Test that the Escape key is handled as an exit fullscreen command while the
351 // Flash widget has the focus.
352 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
354 EXPECT_TRUE(ObserveTabIsInFullscreen(false));