summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormiu <miu@chromium.org>2014-10-24 19:39:04 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-25 02:39:21 +0000
commit9e14e49082f175ceccaa93a3329a8816b905ae34 (patch)
treeccf163598dcb56c273407275c4854e191cb83611 /chrome
parent399aec1a45cfbcc2feb04867f03930df6628ab24 (diff)
downloadchromium_src-9e14e49082f175ceccaa93a3329a8816b905ae34.zip
chromium_src-9e14e49082f175ceccaa93a3329a8816b905ae34.tar.gz
chromium_src-9e14e49082f175ceccaa93a3329a8816b905ae34.tar.bz2
Fix Flash fullscreen focus regressions, and add interactive_ui_tests.
This revisits focus handling logic relating to Flash fullscreen in WebContentsImpl. Before this change, calls to WebContentsImpl::Focus() would directly call RenderWidgetHostView::Focus(), but this prevented WebContentsView and its delegate from "shadowing" these focus changes in the browser UI. In other words, on Windows/Linux/CrOS, the focus state for widgets in the ui::views toolkit were not necessarily synchronized with the render widget's focus state (see bug for details). 90% of this change adds regression testing for everything described above. The tests run a simulated fullscreened Flash widget, and then invoke user keyboard and mouse actions on the browser UI to confirm both the Flash widget and ui::views are in agreement and respond correctly to focus changes; also, including focus store/restore when switching tabs. BUG=403641 TEST=Repro steps in bug 403641; and also that focus remains with the omnibox or tab contents when switching back and forth to other tabs. New automated interactive_ui_tests. Review URL: https://codereview.chromium.org/670653005 Cr-Commit-Position: refs/heads/master@{#301269}
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/ui/fullscreen/flash_fullscreen_interactive_browsertest.cc348
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/interactive_ui_tests.isolate5
3 files changed, 354 insertions, 0 deletions
diff --git a/chrome/browser/ui/fullscreen/flash_fullscreen_interactive_browsertest.cc b/chrome/browser/ui/fullscreen/flash_fullscreen_interactive_browsertest.cc
new file mode 100644
index 0000000..db7f86b
--- /dev/null
+++ b/chrome/browser/ui/fullscreen/flash_fullscreen_interactive_browsertest.cc
@@ -0,0 +1,348 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/ppapi/ppapi_test.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_utils.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace {
+
+#if defined(OS_MACOSX)
+const bool kIsMacUI = true;
+#else
+const bool kIsMacUI = false;
+#endif
+
+// Runs the current MessageLoop until |condition| is true or timeout.
+bool RunLoopUntil(const base::Callback<bool()>& condition) {
+ const base::TimeTicks start_time = base::TimeTicks::Now();
+ while (!condition.Run()) {
+ const base::TimeTicks current_time = base::TimeTicks::Now();
+ if (current_time - start_time > base::TimeDelta::FromSeconds(10)) {
+ ADD_FAILURE() << "Condition not met within ten seconds.";
+ return false;
+ }
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(20));
+ content::RunMessageLoop();
+ }
+ return true;
+}
+
+} // namespace
+
+// A BrowserTest that opens a test page that launches a simulated fullscreen
+// Flash plugin. The plugin responds to mouse clicks and key presses by
+// changing color. Once launched, the browser UI can be tested to confirm the
+// desired interactive behaviors.
+class FlashFullscreenInteractiveBrowserTest : public OutOfProcessPPAPITest {
+ public:
+ FlashFullscreenInteractiveBrowserTest() {}
+ virtual ~FlashFullscreenInteractiveBrowserTest() {}
+
+ protected:
+ content::WebContents* GetActiveWebContents() const {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ // A simple way to convince libcontent and the browser UI that a tab is being
+ // screen captured. During tab capture, Flash fullscreen remains embedded
+ // within the tab content area of a non-fullscreened browser window.
+ void StartFakingTabCapture() {
+ GetActiveWebContents()->IncrementCapturerCount(gfx::Size(360, 240));
+ }
+
+ bool LaunchFlashFullscreen() {
+ // This navigates to a page that runs the simulated fullscreen Flash
+ // plugin. It will block until the plugin has completed an attempt to enter
+ // Flash fullscreen mode.
+ OutOfProcessPPAPITest::RunTest("FlashFullscreenForBrowserUI");
+
+ if (::testing::Test::HasFailure()) {
+ ADD_FAILURE() << ("Failed to launch simulated fullscreen Flash plugin. "
+ "Interactive UI testing cannot proceed.");
+ return false;
+ }
+
+ EXPECT_TRUE(ObserveTabIsInFullscreen(true));
+
+ return !::testing::Test::HasFailure();
+ }
+
+ void UseAcceleratorToOpenNewTab() {
+ content::WebContents* const old_tab_contents = GetActiveWebContents();
+ EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
+ browser(), ui::VKEY_T, !kIsMacUI, false, false, kIsMacUI));
+ EXPECT_TRUE(RunLoopUntil(base::Bind(
+ &FlashFullscreenInteractiveBrowserTest::IsObservingActiveWebContents,
+ base::Unretained(this),
+ old_tab_contents,
+ false)));
+ }
+
+ void UseAcceleratorToSwitchToTab(int tab_index) {
+ content::WebContents* const old_tab_contents = GetActiveWebContents();
+ const ui::KeyboardCode key_code =
+ static_cast<ui::KeyboardCode>(ui::VKEY_1 + tab_index);
+ EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
+ browser(), key_code, !kIsMacUI, false, false, kIsMacUI));
+ EXPECT_TRUE(RunLoopUntil(base::Bind(
+ &FlashFullscreenInteractiveBrowserTest::IsObservingActiveWebContents,
+ base::Unretained(this),
+ old_tab_contents,
+ false)));
+ }
+
+ void PressEscape() {
+ EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
+ browser(), ui::VKEY_ESCAPE, false, false, false, false));
+ }
+
+ void PressSpacebar() {
+ EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
+ browser(), ui::VKEY_SPACE, false, false, false, false));
+ }
+
+ void SpamSpacebar() {
+ for (int i = 0; i < 11; ++i)
+ PressSpacebar();
+ }
+
+ void ClickOnTabContainer() {
+ ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER);
+ }
+
+ void ClickOnOmnibox() {
+ ui_test_utils::ClickOnView(browser(), VIEW_ID_OMNIBOX);
+ }
+
+ bool ObserveTabIsInFullscreen(bool expected_in_fullscreen) const {
+ if (!RunLoopUntil(base::Bind(
+ &FlashFullscreenInteractiveBrowserTest::IsObservingTabInFullscreen,
+ base::Unretained(this),
+ GetActiveWebContents(),
+ expected_in_fullscreen)))
+ return false;
+
+ if (expected_in_fullscreen) {
+ if (!GetActiveWebContents()->GetFullscreenRenderWidgetHostView()) {
+ ADD_FAILURE()
+ << "WebContents should have a fullscreen RenderWidgetHostView.";
+ return false;
+ }
+ EXPECT_EQ(GetActiveWebContents()->GetCapturerCount() > 0,
+ !browser()->fullscreen_controller()->
+ IsWindowFullscreenForTabOrPending());
+ }
+
+ return true;
+ }
+
+ bool ObserveFlashHasFocus(content::WebContents* contents,
+ bool expected_to_have_focus) const {
+ if (!RunLoopUntil(base::Bind(
+ &FlashFullscreenInteractiveBrowserTest::IsObservingFlashHasFocus,
+ base::Unretained(this),
+ contents,
+ expected_to_have_focus)))
+ return false;
+
+ if (expected_to_have_focus) {
+ content::RenderWidgetHostView* const web_page_view =
+ contents->GetRenderWidgetHostView();
+ EXPECT_FALSE(web_page_view && web_page_view->HasFocus())
+ << "Both RenderWidgetHostViews cannot have focus at the same time.";
+
+ if (contents == GetActiveWebContents())
+ EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(),
+ VIEW_ID_TAB_CONTAINER));
+ }
+
+ return true;
+ }
+
+ bool ObserveFlashFillColor(SkColor expected_color) const {
+ return RunLoopUntil(base::Bind(
+ &FlashFullscreenInteractiveBrowserTest::IsObservingFlashFillColor,
+ base::Unretained(this),
+ expected_color));
+ }
+
+ private:
+ bool IsObservingTabInFullscreen(content::WebContents* contents,
+ bool expected_in_fullscreen) const {
+ return expected_in_fullscreen == browser()->fullscreen_controller()->
+ IsFullscreenForTabOrPending(contents);
+ }
+
+ bool IsObservingFlashHasFocus(content::WebContents* contents,
+ bool expected_to_have_focus) const {
+ content::RenderWidgetHostView* const flash_fs_view =
+ contents->GetFullscreenRenderWidgetHostView();
+ const bool flash_has_focus = flash_fs_view && flash_fs_view->HasFocus();
+ return flash_has_focus == expected_to_have_focus;
+ }
+
+ bool IsObservingActiveWebContents(content::WebContents* contents,
+ bool expected_active_contents) const {
+ return (contents == GetActiveWebContents()) == expected_active_contents;
+ }
+
+ bool IsObservingFlashFillColor(SkColor expected_color) const {
+ content::RenderWidgetHostView* const flash_fs_view =
+ GetActiveWebContents()->GetFullscreenRenderWidgetHostView();
+ content::RenderWidgetHost* const flash_fs_host =
+ flash_fs_view ? flash_fs_view->GetRenderWidgetHost() : nullptr;
+ if (!flash_fs_host) {
+ ADD_FAILURE() << "Flash fullscreen RenderWidgetHost is gone.";
+ return false;
+ }
+
+ // When a widget is first shown, it can take some time before it is ready
+ // for copying from its backing store. This is a transient condition, and
+ // so it is not being treated as a test failure.
+ if (!flash_fs_host->CanCopyFromBackingStore())
+ return false;
+
+ // Copy and examine the upper-left pixel of the widget and compare it to the
+ // |expected_color|.
+ bool is_expected_color = false;
+ flash_fs_host->CopyFromBackingStore(
+ gfx::Rect(0, 0, 1, 1),
+ gfx::Size(1, 1),
+ base::Bind(
+ &FlashFullscreenInteractiveBrowserTest::CheckBitmapForFillColor,
+ expected_color,
+ &is_expected_color,
+ base::MessageLoop::QuitClosure()),
+ kN32_SkColorType);
+ content::RunMessageLoop();
+
+ return is_expected_color;
+ }
+
+ static void CheckBitmapForFillColor(SkColor expected_color,
+ bool* is_expected_color,
+ const base::Closure& done_cb,
+ bool success,
+ const SkBitmap& bitmap) {
+ if (success) {
+ SkAutoLockPixels lock_pixels(bitmap);
+ if (bitmap.width() > 0 && bitmap.height() > 0)
+ *is_expected_color = (bitmap.getColor(0, 0) == expected_color);
+ }
+ done_cb.Run();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(FlashFullscreenInteractiveBrowserTest);
+};
+
+// Tests that launching and exiting fullscreen-within-tab works.
+IN_PROC_BROWSER_TEST_F(FlashFullscreenInteractiveBrowserTest,
+ FullscreenWithinTab_EscapeKeyExitsFullscreen) {
+ ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+ StartFakingTabCapture();
+ ASSERT_TRUE(LaunchFlashFullscreen());
+ content::WebContents* const first_tab_contents = GetActiveWebContents();
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, true));
+ PressEscape();
+ EXPECT_TRUE(ObserveTabIsInFullscreen(false));
+}
+
+// This tests that browser UI focus behavior is correct when switching between
+// tabs; particularly, that that focus between the omnibox and tab contents is
+// stored/restored correctly. Mouse and keyboard events are used to confirm
+// that the widget the UI thinks is focused is the one that responds to these
+// input events.
+IN_PROC_BROWSER_TEST_F(FlashFullscreenInteractiveBrowserTest,
+ FullscreenWithinTab_FocusWhenSwitchingTabs) {
+ ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+ StartFakingTabCapture();
+ ASSERT_TRUE(LaunchFlashFullscreen());
+
+ // Upon entering fullscreen, the Flash widget should have focus and be filled
+ // with green.
+ content::WebContents* const first_tab_contents = GetActiveWebContents();
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, true));
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorGREEN));
+
+ // Pressing the spacebar on the keyboard should change the fill color to red
+ // to indicate the plugin truly does have the keyboard focus. Clicking on the
+ // view should change the fill color to blue.
+ PressSpacebar();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED));
+ ClickOnTabContainer();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+
+ // Launch a new tab. The Flash widget should have lost focus.
+ UseAcceleratorToOpenNewTab();
+ content::WebContents* const second_tab_contents = GetActiveWebContents();
+ ASSERT_NE(first_tab_contents, second_tab_contents);
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, false));
+ ClickOnOmnibox();
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, false));
+ SpamSpacebar();
+
+ // Switch back to first tab. The plugin should not have responded to the key
+ // presses above (while the omnibox was focused), and should regain focus only
+ // now. Poke it with key and mouse events to confirm.
+ UseAcceleratorToSwitchToTab(0);
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, true));
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+ PressSpacebar();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED));
+ ClickOnTabContainer();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+
+ // Click on the omnibox while still in the first tab, and the Flash widget
+ // should lose focus. Key presses should not affect the color of the Flash
+ // widget.
+ ClickOnOmnibox();
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, false));
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+ SpamSpacebar();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+
+ // Switch to the second tab, click on the web page content, and then go back
+ // to the first tab. Focus should have been restored to the omnibox when
+ // going back to the first tab, and so key presses should not change the color
+ // of the Flash widget.
+ UseAcceleratorToSwitchToTab(1);
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, false));
+ ClickOnTabContainer();
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, false));
+ UseAcceleratorToSwitchToTab(0);
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, false));
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+ SpamSpacebar();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+
+ // Clicking on the Flash widget should give it focus again.
+ ClickOnTabContainer();
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, true));
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorRED));
+ PressSpacebar();
+ ASSERT_TRUE(ObserveFlashFillColor(SK_ColorBLUE));
+
+ // Test that the Escape key is handled as an exit fullscreen command while the
+ // Flash widget has the focus.
+ EXPECT_TRUE(ObserveFlashHasFocus(first_tab_contents, true));
+ PressEscape();
+ EXPECT_TRUE(ObserveTabIsInFullscreen(false));
+}
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index fd4e170..445ee5c 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -864,6 +864,7 @@
'browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.cc',
'browser/ui/cocoa/panels/panel_cocoa_browsertest.mm',
'browser/ui/find_bar/find_bar_host_interactive_uitest.cc',
+ 'browser/ui/fullscreen/flash_fullscreen_interactive_browsertest.cc',
'browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc',
'browser/ui/fullscreen/fullscreen_controller_state_interactive_browsertest.cc',
'browser/ui/omnibox/omnibox_view_browsertest.cc',
diff --git a/chrome/interactive_ui_tests.isolate b/chrome/interactive_ui_tests.isolate
index 9f2717d..df3893d 100644
--- a/chrome/interactive_ui_tests.isolate
+++ b/chrome/interactive_ui_tests.isolate
@@ -17,6 +17,7 @@
'../testing/xvfb.py',
'<(PRODUCT_DIR)/libffmpegsumo.so',
'<(PRODUCT_DIR)/libosmesa.so',
+ '<(PRODUCT_DIR)/libppapi_tests.so',
'<(PRODUCT_DIR)/pyproto/',
],
},
@@ -33,6 +34,8 @@
'files': [
'../net/data/ssl/certificates/',
'../net/tools/testserver/',
+ '../ppapi/tests/test_case.html',
+ '../ppapi/tests/test_page.css',
'../testing/test_env.py',
'../third_party/pyftpdlib/',
'../third_party/pywebsocket/',
@@ -61,6 +64,7 @@
'<(PRODUCT_DIR)/<(mac_product_name).app/',
'<(PRODUCT_DIR)/ffmpegsumo.so',
'<(PRODUCT_DIR)/osmesa.so',
+ '<(PRODUCT_DIR)/ppapi_tests.plugin/Contents/MacOS/ppapi_tests',
],
},
}],
@@ -85,6 +89,7 @@
'<(PRODUCT_DIR)/libGLESv2.dll',
'<(PRODUCT_DIR)/osmesa.dll',
'<(PRODUCT_DIR)/plugins/npapi_test_plugin.dll',
+ '<(PRODUCT_DIR)/ppapi_tests.dll',
],
},
}],