blob: a430357e1da993ab64a2e1ef7bdb76dd31f78782 [file] [log] [blame]
// Copyright (c) 2012 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/memory/weak_ptr.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/constrained_window_views.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/web_modal/web_contents_modal_dialog_host.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "ipc/ipc_message.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/test/test_widget_observer.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/web_dialogs/test/test_web_dialog_delegate.h"
#if defined(USE_AURA) && defined(USE_X11)
#include <X11/Xlib.h>
#include "ui/events/x/events_x_utils.h"
#endif
using web_modal::WebContentsModalDialogManager;
using web_modal::WebContentsModalDialogManagerDelegate;
namespace {
class TestConstrainedDialogContentsView
: public views::View,
public base::SupportsWeakPtr<TestConstrainedDialogContentsView> {
public:
TestConstrainedDialogContentsView()
: text_field_(new views::Textfield) {
SetLayoutManager(new views::FillLayout);
AddChildView(text_field_);
}
views::View* GetInitiallyFocusedView() {
return text_field_;
}
private:
views::Textfield* text_field_;
DISALLOW_COPY_AND_ASSIGN(TestConstrainedDialogContentsView);
};
class TestConstrainedDialog : public views::DialogDelegate {
public:
TestConstrainedDialog()
: contents_((new TestConstrainedDialogContentsView())->AsWeakPtr()),
done_(false) {
}
virtual ~TestConstrainedDialog() {}
virtual views::View* GetInitiallyFocusedView() OVERRIDE {
return contents_ ? contents_->GetInitiallyFocusedView() : NULL;
}
virtual views::View* GetContentsView() OVERRIDE {
return contents_.get();
}
virtual views::Widget* GetWidget() OVERRIDE {
return contents_ ? contents_->GetWidget() : NULL;
}
virtual const views::Widget* GetWidget() const OVERRIDE {
return contents_ ? contents_->GetWidget() : NULL;
}
virtual void DeleteDelegate() OVERRIDE {
// Don't delete the delegate yet. We need to keep it around for inspection
// later.
EXPECT_TRUE(done_);
}
virtual bool Accept() OVERRIDE {
done_ = true;
return true;
}
virtual bool Cancel() OVERRIDE {
done_ = true;
return true;
}
virtual ui::ModalType GetModalType() const OVERRIDE {
#if defined(USE_ASH)
return ui::MODAL_TYPE_CHILD;
#else
return views::WidgetDelegate::GetModalType();
#endif
}
bool done() {
return done_;
}
private:
// contents_ will be freed when the View goes away.
base::WeakPtr<TestConstrainedDialogContentsView> contents_;
bool done_;
DISALLOW_COPY_AND_ASSIGN(TestConstrainedDialog);
};
} // namespace
class ConstrainedWindowViewTest : public InProcessBrowserTest {
public:
ConstrainedWindowViewTest() {
}
};
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
// TODO(erg): linux_aura bringup: http://crbug.com/163931
#define MAYBE_FocusTest DISABLED_FocusTest
#else
#define MAYBE_FocusTest FocusTest
#endif
// Tests the following:
//
// *) Initially focused view in a constrained dialog receives focus reliably.
//
// *) Constrained windows that are queued don't register themselves as
// accelerator targets until they are displayed.
IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, MAYBE_FocusTest) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents != NULL);
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
WebContentsModalDialogManager::FromWebContents(web_contents);
ASSERT_TRUE(web_contents_modal_dialog_manager != NULL);
WebContentsModalDialogManagerDelegate* modal_delegate =
web_contents_modal_dialog_manager->delegate();
ASSERT_TRUE(modal_delegate != NULL);
// Create a constrained dialog. It will attach itself to web_contents.
scoped_ptr<TestConstrainedDialog> test_dialog1(new TestConstrainedDialog);
views::Widget* window1 = views::Widget::CreateWindowAsFramelessChild(
test_dialog1.get(),
web_contents->GetView()->GetNativeView(),
modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
web_contents_modal_dialog_manager->ShowDialog(window1->GetNativeView());
views::FocusManager* focus_manager = window1->GetFocusManager();
ASSERT_TRUE(focus_manager);
// test_dialog1's text field should be focused.
EXPECT_EQ(test_dialog1->GetInitiallyFocusedView(),
focus_manager->GetFocusedView());
// Now create a second constrained dialog. This will also be attached to
// web_contents, but will remain hidden since the test_dialog1 is still
// showing.
scoped_ptr<TestConstrainedDialog> test_dialog2(new TestConstrainedDialog);
views::Widget* window2 = views::Widget::CreateWindowAsFramelessChild(
test_dialog2.get(),
web_contents->GetView()->GetNativeView(),
modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
web_contents_modal_dialog_manager->ShowDialog(window2->GetNativeView());
// Should be the same focus_manager.
ASSERT_EQ(focus_manager, window2->GetFocusManager());
// test_dialog1's text field should still be the view that has focus.
EXPECT_EQ(test_dialog1->GetInitiallyFocusedView(),
focus_manager->GetFocusedView());
ASSERT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
// Now send a VKEY_RETURN to the browser. This should result in closing
// test_dialog1.
EXPECT_TRUE(focus_manager->ProcessAccelerator(
ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)));
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(test_dialog1->done());
EXPECT_FALSE(test_dialog2->done());
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
// test_dialog2 will be shown. Focus should be on test_dialog2's text field.
EXPECT_EQ(test_dialog2->GetInitiallyFocusedView(),
focus_manager->GetFocusedView());
int tab_with_constrained_window =
browser()->tab_strip_model()->active_index();
// Create a new tab.
chrome::NewTab(browser());
// The constrained dialog should no longer be selected.
EXPECT_NE(test_dialog2->GetInitiallyFocusedView(),
focus_manager->GetFocusedView());
browser()->tab_strip_model()->ActivateTabAt(tab_with_constrained_window,
false);
// Activating the previous tab should bring focus to the constrained window.
EXPECT_EQ(test_dialog2->GetInitiallyFocusedView(),
focus_manager->GetFocusedView());
// Send another VKEY_RETURN, closing test_dialog2
EXPECT_TRUE(focus_manager->ProcessAccelerator(
ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)));
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(test_dialog2->done());
EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive());
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
// TODO(erg): linux_aura bringup: http://crbug.com/163931
#define MAYBE_TabCloseTest DISABLED_TabCloseTest
#else
#define MAYBE_TabCloseTest TabCloseTest
#endif
// Tests that the constrained window is closed properly when its tab is
// closed.
IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, MAYBE_TabCloseTest) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents != NULL);
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
WebContentsModalDialogManager::FromWebContents(web_contents);
ASSERT_TRUE(web_contents_modal_dialog_manager != NULL);
WebContentsModalDialogManagerDelegate* modal_delegate =
web_contents_modal_dialog_manager->delegate();
ASSERT_TRUE(modal_delegate != NULL);
// Create a constrained dialog. It will attach itself to web_contents.
scoped_ptr<TestConstrainedDialog> test_dialog(new TestConstrainedDialog);
views::Widget* window = views::Widget::CreateWindowAsFramelessChild(
test_dialog.get(),
web_contents->GetView()->GetNativeView(),
modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
web_contents_modal_dialog_manager->ShowDialog(window->GetNativeView());
bool closed =
browser()->tab_strip_model()->CloseWebContentsAt(
browser()->tab_strip_model()->active_index(),
TabStripModel::CLOSE_NONE);
EXPECT_TRUE(closed);
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(test_dialog->done());
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
// TODO(erg): linux_aura bringup: http://crbug.com/163931
#define MAYBE_TabSwitchTest DISABLED_TabSwitchTest
#else
#define MAYBE_TabSwitchTest TabSwitchTest
#endif
// Tests that the constrained window is hidden when an other tab is selected and
// shown when its tab is selected again.
IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, MAYBE_TabSwitchTest) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents != NULL);
// Create a constrained dialog. It will attach itself to web_contents.
scoped_ptr<TestConstrainedDialog> test_dialog(new TestConstrainedDialog);
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
WebContentsModalDialogManager::FromWebContents(web_contents);
WebContentsModalDialogManagerDelegate* modal_delegate =
web_contents_modal_dialog_manager->delegate();
ASSERT_TRUE(modal_delegate != NULL);
views::Widget* window = views::Widget::CreateWindowAsFramelessChild(
test_dialog.get(),
web_contents->GetView()->GetNativeView(),
modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
web_contents_modal_dialog_manager->ShowDialog(window->GetNativeView());
EXPECT_TRUE(window->IsVisible());
// Open a new tab. The constrained window should hide itself.
browser()->tab_strip_model()->AppendWebContents(
content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile())),
true);
EXPECT_FALSE(window->IsVisible());
// Close the new tab. The constrained window should show itself again.
bool closed =
browser()->tab_strip_model()->CloseWebContentsAt(
browser()->tab_strip_model()->active_index(),
TabStripModel::CLOSE_NONE);
EXPECT_TRUE(closed);
EXPECT_TRUE(window->IsVisible());
// Close the original tab.
browser()->tab_strip_model()->CloseWebContentsAt(
browser()->tab_strip_model()->active_index(),
TabStripModel::CLOSE_NONE);
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(test_dialog->done());
}
// Tests that the constrained window behaves properly when moving its tab
// between browser windows.
IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabMoveTest) {
// Open a second browser.
Browser* browser2 = CreateBrowser(browser()->profile());
// Create a second WebContents in the second browser, so that moving the
// WebContents does not trigger the browser to close immediately. This mimics
// the behavior when a user drags tabs between browsers.
content::WebContents* web_contents = content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
browser2->tab_strip_model()->AppendWebContents(web_contents, true);
ASSERT_EQ(web_contents, browser2->tab_strip_model()->GetActiveWebContents());
// Create a constrained dialog. It will attach itself to web_contents.
scoped_ptr<TestConstrainedDialog> test_dialog(new TestConstrainedDialog);
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
WebContentsModalDialogManager::FromWebContents(web_contents);
WebContentsModalDialogManagerDelegate* modal_delegate =
web_contents_modal_dialog_manager->delegate();
ASSERT_TRUE(modal_delegate != NULL);
views::Widget* window = views::Widget::CreateWindowAsFramelessChild(
test_dialog.get(),
web_contents->GetView()->GetNativeView(),
modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
web_contents_modal_dialog_manager->ShowDialog(window->GetNativeView());
EXPECT_TRUE(window->IsVisible());
// Detach the web contents from the second browser's tab strip.
browser2->tab_strip_model()->DetachWebContentsAt(
browser2->tab_strip_model()->GetIndexOfWebContents(web_contents));
// Append the web contents to the first browser.
browser()->tab_strip_model()->AppendWebContents(web_contents, true);
EXPECT_TRUE(window->IsVisible());
// Close the second browser.
browser2->tab_strip_model()->CloseAllTabs();
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(window->IsVisible());
// Close the dialog's tab.
bool closed =
browser()->tab_strip_model()->CloseWebContentsAt(
browser()->tab_strip_model()->GetIndexOfWebContents(web_contents),
TabStripModel::CLOSE_NONE);
EXPECT_TRUE(closed);
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(test_dialog->done());
}
#if defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11))
// Forwards the key event which has |key_code| to the renderer.
void ForwardKeyEvent(content::RenderViewHost* host, ui::KeyboardCode key_code) {
#if defined(OS_WIN)
MSG native_key_event = { NULL, WM_KEYDOWN, key_code, 0 };
#elif defined(USE_X11)
XEvent x_event;
ui::InitXKeyEventForTesting(
ui::ET_KEY_PRESSED, key_code, ui::EF_NONE, &x_event);
XEvent* native_key_event = &x_event;
#endif
#if defined(USE_AURA)
ui::KeyEvent key(native_key_event, false);
ui::KeyEvent* native_ui_key_event = &key;
#elif defined(OS_WIN)
MSG native_ui_key_event = native_key_event;
#endif
host->ForwardKeyboardEvent(
content::NativeWebKeyboardEvent(native_ui_key_event));
}
// Tests that backspace is not processed before it's sent to the web contents.
// Flaky on Win Aura and Linux ChromiumOS. See http://crbug.com/170331
#if defined(USE_AURA)
#define MAYBE_BackspaceSentToWebContent DISABLED_BackspaceSentToWebContent
#else
#define MAYBE_BackspaceSentToWebContent BackspaceSentToWebContent
#endif
IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest,
MAYBE_BackspaceSentToWebContent) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents != NULL);
GURL new_tab_url(chrome::kChromeUINewTabURL);
ui_test_utils::NavigateToURL(browser(), new_tab_url);
GURL about_url(chrome::kChromeUIAboutURL);
ui_test_utils::NavigateToURL(browser(), about_url);
ConstrainedWebDialogDelegate* cwdd = CreateConstrainedWebDialog(
browser()->profile(),
new ui::test::TestWebDialogDelegate(about_url),
NULL,
web_contents);
content::WindowedNotificationObserver back_observer(
content::NOTIFICATION_LOAD_STOP,
content::Source<content::NavigationController>(
&web_contents->GetController()));
content::RenderViewHost* render_view_host =
cwdd->GetWebContents()->GetRenderViewHost();
ForwardKeyEvent(render_view_host, ui::VKEY_BACK);
// Backspace is not processed as accelerator before it's sent to web contents.
EXPECT_FALSE(web_contents->GetController().GetPendingEntry());
EXPECT_EQ(about_url.spec(), web_contents->GetURL().spec());
// Backspace is processed as accelerator after it's sent to web contents.
content::RunAllPendingInMessageLoop();
EXPECT_TRUE(web_contents->GetController().GetPendingEntry());
// Wait for the navigation to commit, since the URL will not be visible
// until then.
back_observer.Wait();
EXPECT_TRUE(chrome::IsNTPURL(web_contents->GetURL(), browser()->profile()));
}
// Fails flakily (once per 10-20 runs) on Win Aura only. http://crbug.com/177482
// Also fails on CrOS.
// Also fails on linux_aura (http://crbug.com/163931)
#if defined(TOOLKIT_VIEWS)
#define MAYBE_EscapeCloseConstrainedWindow DISABLED_EscapeCloseConstrainedWindow
#else
#define MAYBE_EscapeCloseConstrainedWindow EscapeCloseConstrainedWindow
#endif
// Tests that escape closes the constrained window.
IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest,
MAYBE_EscapeCloseConstrainedWindow) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents != NULL);
GURL new_tab_url(chrome::kChromeUINewTabURL);
ui_test_utils::NavigateToURL(browser(), new_tab_url);
ConstrainedWebDialogDelegate* cwdd = CreateConstrainedWebDialog(
browser()->profile(),
new ui::test::TestWebDialogDelegate(new_tab_url),
NULL,
web_contents);
views::Widget* widget =
views::Widget::GetWidgetForNativeView(cwdd->GetNativeDialog());
views::test::TestWidgetObserver observer(widget);
content::RenderViewHost* render_view_host =
cwdd->GetWebContents()->GetRenderViewHost();
ForwardKeyEvent(render_view_host, ui::VKEY_ESCAPE);
// Escape is not processed as accelerator before it's sent to web contents.
EXPECT_FALSE(observer.widget_closed());
content::RunAllPendingInMessageLoop();
// Escape is processed as accelerator after it's sent to web contents.
EXPECT_TRUE(observer.widget_closed());
}
#endif // defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11))