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()->task_runner()->PostDelayedTask(
40 FROM_HERE
, base::MessageLoop::QuitClosure(),
41 base::TimeDelta::FromMilliseconds(20));
42 content::RunMessageLoop();
49 // A BrowserTest that opens a test page that launches a simulated fullscreen
50 // Flash plugin. The plugin responds to mouse clicks and key presses by
51 // changing color. Once launched, the browser UI can be tested to confirm the
52 // desired interactive behaviors.
53 class FlashFullscreenInteractiveBrowserTest
: public OutOfProcessPPAPITest
{
55 FlashFullscreenInteractiveBrowserTest() {}
56 ~FlashFullscreenInteractiveBrowserTest() override
{}
59 content::WebContents
* GetActiveWebContents() const {
60 return browser()->tab_strip_model()->GetActiveWebContents();
63 // A simple way to convince libcontent and the browser UI that a tab is being
64 // screen captured. During tab capture, Flash fullscreen remains embedded
65 // within the tab content area of a non-fullscreened browser window.
66 void StartFakingTabCapture() {
67 GetActiveWebContents()->IncrementCapturerCount(gfx::Size(360, 240));
70 bool LaunchFlashFullscreen() {
71 // This navigates to a page that runs the simulated fullscreen Flash
72 // plugin. It will block until the plugin has completed an attempt to enter
73 // Flash fullscreen mode.
74 OutOfProcessPPAPITest::RunTest("FlashFullscreenForBrowserUI");
76 if (::testing::Test::HasFailure()) {
77 ADD_FAILURE() << ("Failed to launch simulated fullscreen Flash plugin. "
78 "Interactive UI testing cannot proceed.");
82 EXPECT_TRUE(ObserveTabIsInFullscreen(true));
84 return !::testing::Test::HasFailure();
87 void UseAcceleratorToOpenNewTab() {
88 content::WebContents
* const old_tab_contents
= GetActiveWebContents();
89 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
90 browser(), ui::VKEY_T
, !kIsMacUI
, false, false, kIsMacUI
));
91 EXPECT_TRUE(RunLoopUntil(base::Bind(
92 &FlashFullscreenInteractiveBrowserTest::IsObservingActiveWebContents
,
93 base::Unretained(this),
98 void UseAcceleratorToSwitchToTab(int tab_index
) {
99 content::WebContents
* const old_tab_contents
= GetActiveWebContents();
100 const ui::KeyboardCode key_code
=
101 static_cast<ui::KeyboardCode
>(ui::VKEY_1
+ tab_index
);
102 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
103 browser(), key_code
, !kIsMacUI
, false, false, kIsMacUI
));
104 EXPECT_TRUE(RunLoopUntil(base::Bind(
105 &FlashFullscreenInteractiveBrowserTest::IsObservingActiveWebContents
,
106 base::Unretained(this),
112 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
113 browser(), ui::VKEY_ESCAPE
, false, false, false, false));
116 void PressSpacebar() {
117 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
118 browser(), ui::VKEY_SPACE
, false, false, false, false));
121 void SpamSpacebar() {
122 for (int i
= 0; i
< 11; ++i
)
126 void ClickOnTabContainer() {
127 ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER
);
130 void ClickOnOmnibox() {
131 ui_test_utils::ClickOnView(browser(), VIEW_ID_OMNIBOX
);
134 bool ObserveTabIsInFullscreen(bool expected_in_fullscreen
) const {
135 if (!RunLoopUntil(base::Bind(
136 &FlashFullscreenInteractiveBrowserTest::IsObservingTabInFullscreen
,
137 base::Unretained(this),
138 GetActiveWebContents(),
139 expected_in_fullscreen
)))
142 if (expected_in_fullscreen
) {
143 if (!GetActiveWebContents()->GetFullscreenRenderWidgetHostView()) {
145 << "WebContents should have a fullscreen RenderWidgetHostView.";
148 EXPECT_EQ(GetActiveWebContents()->GetCapturerCount() > 0,
150 ->exclusive_access_manager()
151 ->fullscreen_controller()
152 ->IsWindowFullscreenForTabOrPending());
158 bool ObserveFlashHasFocus(content::WebContents
* contents
,
159 bool expected_to_have_focus
) const {
160 if (!RunLoopUntil(base::Bind(
161 &FlashFullscreenInteractiveBrowserTest::IsObservingFlashHasFocus
,
162 base::Unretained(this),
164 expected_to_have_focus
)))
167 if (expected_to_have_focus
) {
168 content::RenderWidgetHostView
* const web_page_view
=
169 contents
->GetRenderWidgetHostView();
170 EXPECT_FALSE(web_page_view
&& web_page_view
->HasFocus())
171 << "Both RenderWidgetHostViews cannot have focus at the same time.";
173 if (contents
== GetActiveWebContents())
174 EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(),
175 VIEW_ID_TAB_CONTAINER
));
181 bool ObserveFlashFillColor(SkColor expected_color
) const {
182 return RunLoopUntil(base::Bind(
183 &FlashFullscreenInteractiveBrowserTest::IsObservingFlashFillColor
,
184 base::Unretained(this),
189 bool IsObservingTabInFullscreen(content::WebContents
* contents
,
190 bool expected_in_fullscreen
) const {
191 return expected_in_fullscreen
==
193 ->exclusive_access_manager()
194 ->fullscreen_controller()
195 ->IsFullscreenForTabOrPending(contents
);
198 bool IsObservingFlashHasFocus(content::WebContents
* contents
,
199 bool expected_to_have_focus
) const {
200 content::RenderWidgetHostView
* const flash_fs_view
=
201 contents
->GetFullscreenRenderWidgetHostView();
202 const bool flash_has_focus
= flash_fs_view
&& flash_fs_view
->HasFocus();
203 return flash_has_focus
== expected_to_have_focus
;
206 bool IsObservingActiveWebContents(content::WebContents
* contents
,
207 bool expected_active_contents
) const {
208 return (contents
== GetActiveWebContents()) == expected_active_contents
;
211 bool IsObservingFlashFillColor(SkColor expected_color
) const {
212 content::RenderWidgetHostView
* const flash_fs_view
=
213 GetActiveWebContents()->GetFullscreenRenderWidgetHostView();
214 content::RenderWidgetHost
* const flash_fs_host
=
215 flash_fs_view
? flash_fs_view
->GetRenderWidgetHost() : nullptr;
216 if (!flash_fs_host
) {
217 ADD_FAILURE() << "Flash fullscreen RenderWidgetHost is gone.";
221 // When a widget is first shown, it can take some time before it is ready
222 // for copying from its backing store. This is a transient condition, and
223 // so it is not being treated as a test failure.
224 if (!flash_fs_host
->CanCopyFromBackingStore())
227 // Copy and examine the upper-left pixel of the widget and compare it to the
229 bool is_expected_color
= false;
230 flash_fs_host
->CopyFromBackingStore(
231 gfx::Rect(0, 0, 1, 1),
234 &FlashFullscreenInteractiveBrowserTest::CheckBitmapForFillColor
,
237 base::MessageLoop::QuitClosure()),
239 content::RunMessageLoop();
241 return is_expected_color
;
244 static void CheckBitmapForFillColor(SkColor expected_color
,
245 bool* is_expected_color
,
246 const base::Closure
& done_cb
,
247 const SkBitmap
& bitmap
,
248 content::ReadbackResponse response
) {
249 if (response
== content::READBACK_SUCCESS
) {
250 SkAutoLockPixels
lock_pixels(bitmap
);
251 if (bitmap
.width() > 0 && bitmap
.height() > 0)
252 *is_expected_color
= (bitmap
.getColor(0, 0) == expected_color
);
257 DISALLOW_COPY_AND_ASSIGN(FlashFullscreenInteractiveBrowserTest
);
260 // Tests that launching and exiting fullscreen-within-tab works.
261 IN_PROC_BROWSER_TEST_F(FlashFullscreenInteractiveBrowserTest
,
262 FullscreenWithinTab_EscapeKeyExitsFullscreen
) {
263 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
264 StartFakingTabCapture();
265 ASSERT_TRUE(LaunchFlashFullscreen());
266 content::WebContents
* const first_tab_contents
= GetActiveWebContents();
267 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
269 EXPECT_TRUE(ObserveTabIsInFullscreen(false));
272 // This tests that browser UI focus behavior is correct when switching between
273 // tabs; particularly, that that focus between the omnibox and tab contents is
274 // stored/restored correctly. Mouse and keyboard events are used to confirm
275 // that the widget the UI thinks is focused is the one that responds to these
278 // Flaky, see http://crbug.com/444476
279 IN_PROC_BROWSER_TEST_F(FlashFullscreenInteractiveBrowserTest
,
280 DISABLED_FullscreenWithinTab_FocusWhenSwitchingTabs
) {
281 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
282 StartFakingTabCapture();
283 ASSERT_TRUE(LaunchFlashFullscreen());
285 // Upon entering fullscreen, the Flash widget should have focus and be filled
287 content::WebContents
* const first_tab_contents
= GetActiveWebContents();
288 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
289 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorGREEN
));
291 // Pressing the spacebar on the keyboard should change the fill color to red
292 // to indicate the plugin truly does have the keyboard focus. Clicking on the
293 // view should change the fill color to blue.
295 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED
));
296 ClickOnTabContainer();
297 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
299 // Launch a new tab. The Flash widget should have lost focus.
300 UseAcceleratorToOpenNewTab();
301 content::WebContents
* const second_tab_contents
= GetActiveWebContents();
302 ASSERT_NE(first_tab_contents
, second_tab_contents
);
303 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
305 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
308 // Switch back to first tab. The plugin should not have responded to the key
309 // presses above (while the omnibox was focused), and should regain focus only
310 // now. Poke it with key and mouse events to confirm.
311 UseAcceleratorToSwitchToTab(0);
312 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
313 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
315 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED
));
316 ClickOnTabContainer();
317 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
319 // Click on the omnibox while still in the first tab, and the Flash widget
320 // should lose focus. Key presses should not affect the color of the Flash
323 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
324 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
326 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
328 // Switch to the second tab, click on the web page content, and then go back
329 // to the first tab. Focus should have been restored to the omnibox when
330 // going back to the first tab, and so key presses should not change the color
331 // of the Flash widget.
332 UseAcceleratorToSwitchToTab(1);
333 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
334 ClickOnTabContainer();
335 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
336 UseAcceleratorToSwitchToTab(0);
337 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, false));
338 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
340 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
342 // Clicking on the Flash widget should give it focus again.
343 ClickOnTabContainer();
344 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
345 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED
));
347 ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE
));
349 // Test that the Escape key is handled as an exit fullscreen command while the
350 // Flash widget has the focus.
351 EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents
, true));
353 EXPECT_TRUE(ObserveTabIsInFullscreen(false));