1 // Copyright (c) 2012 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.
5 #include "base/memory/weak_ptr.h"
6 #include "chrome/browser/platform_util.h"
7 #include "chrome/browser/profiles/profile.h"
8 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/browser_commands.h"
10 #include "chrome/browser/ui/tabs/tab_strip_model.h"
11 #include "chrome/browser/ui/views/constrained_window_views.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/web_contents_modal_dialog_manager.h"
14 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
15 #include "chrome/common/url_constants.h"
16 #include "chrome/test/base/in_process_browser_test.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/browser/native_web_keyboard_event.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_view.h"
22 #include "ipc/ipc_message.h"
23 #include "ui/base/accelerators/accelerator.h"
24 #include "ui/views/controls/textfield/textfield.h"
25 #include "ui/views/focus/focus_manager.h"
26 #include "ui/views/layout/fill_layout.h"
27 #include "ui/views/test/test_widget_observer.h"
28 #include "ui/views/window/dialog_delegate.h"
29 #include "ui/web_dialogs/test/test_web_dialog_delegate.h"
31 #if defined(USE_AURA) && defined(USE_X11)
33 #include "ui/base/x/x11_util.h"
38 class TestConstrainedDialogContentsView
40 public base::SupportsWeakPtr
<TestConstrainedDialogContentsView
> {
42 TestConstrainedDialogContentsView()
43 : text_field_(new views::Textfield
) {
44 SetLayoutManager(new views::FillLayout
);
45 AddChildView(text_field_
);
48 views::View
* GetInitiallyFocusedView() {
53 views::Textfield
* text_field_
;
54 DISALLOW_COPY_AND_ASSIGN(TestConstrainedDialogContentsView
);
57 class TestConstrainedDialog
: public views::DialogDelegate
{
59 TestConstrainedDialog()
60 : contents_((new TestConstrainedDialogContentsView())->AsWeakPtr()),
64 virtual ~TestConstrainedDialog() {}
66 virtual views::View
* GetInitiallyFocusedView() OVERRIDE
{
67 return contents_
? contents_
->GetInitiallyFocusedView() : NULL
;
70 virtual views::View
* GetContentsView() OVERRIDE
{
71 return contents_
.get();
74 virtual views::Widget
* GetWidget() OVERRIDE
{
75 return contents_
? contents_
->GetWidget() : NULL
;
78 virtual const views::Widget
* GetWidget() const OVERRIDE
{
79 return contents_
? contents_
->GetWidget() : NULL
;
82 virtual void DeleteDelegate() OVERRIDE
{
83 // Don't delete the delegate yet. We need to keep it around for inspection
88 virtual bool Accept() OVERRIDE
{
93 virtual bool Cancel() OVERRIDE
{
98 virtual ui::ModalType
GetModalType() const OVERRIDE
{
100 return ui::MODAL_TYPE_CHILD
;
102 return views::WidgetDelegate::GetModalType();
111 // contents_ will be freed when the View goes away.
112 base::WeakPtr
<TestConstrainedDialogContentsView
> contents_
;
115 DISALLOW_COPY_AND_ASSIGN(TestConstrainedDialog
);
120 class ConstrainedWindowViewTest
: public InProcessBrowserTest
{
122 ConstrainedWindowViewTest() {
126 // Tests the following:
128 // *) Initially focused view in a constrained dialog receives focus reliably.
130 // *) Constrained windows that are queued don't register themselves as
131 // accelerator targets until they are displayed.
132 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest
, FocusTest
) {
133 content::WebContents
* web_contents
=
134 browser()->tab_strip_model()->GetActiveWebContents();
135 ASSERT_TRUE(web_contents
!= NULL
);
136 WebContentsModalDialogManager
* web_contents_modal_dialog_manager
=
137 WebContentsModalDialogManager::FromWebContents(web_contents
);
138 ASSERT_TRUE(web_contents_modal_dialog_manager
!= NULL
);
140 // Create a constrained dialog. It will attach itself to web_contents.
141 scoped_ptr
<TestConstrainedDialog
> test_dialog1(new TestConstrainedDialog
);
142 views::Widget
* window1
= CreateWebContentsModalDialogViews(
144 web_contents
->GetView()->GetNativeView());
145 web_contents_modal_dialog_manager
->ShowDialog(window1
->GetNativeView());
147 views::FocusManager
* focus_manager
= window1
->GetFocusManager();
148 ASSERT_TRUE(focus_manager
);
150 // test_dialog1's text field should be focused.
151 EXPECT_EQ(test_dialog1
->GetInitiallyFocusedView(),
152 focus_manager
->GetFocusedView());
154 // Now create a second constrained dialog. This will also be attached to
155 // web_contents, but will remain hidden since the test_dialog1 is still
157 scoped_ptr
<TestConstrainedDialog
> test_dialog2(new TestConstrainedDialog
);
158 views::Widget
* window2
= CreateWebContentsModalDialogViews(
160 web_contents
->GetView()->GetNativeView());
161 web_contents_modal_dialog_manager
->ShowDialog(window2
->GetNativeView());
162 // Should be the same focus_manager.
163 ASSERT_EQ(focus_manager
, window2
->GetFocusManager());
165 // test_dialog1's text field should still be the view that has focus.
166 EXPECT_EQ(test_dialog1
->GetInitiallyFocusedView(),
167 focus_manager
->GetFocusedView());
168 ASSERT_TRUE(web_contents_modal_dialog_manager
->IsShowingDialog());
170 // Now send a VKEY_RETURN to the browser. This should result in closing
172 EXPECT_TRUE(focus_manager
->ProcessAccelerator(
173 ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
)));
174 content::RunAllPendingInMessageLoop();
176 EXPECT_TRUE(test_dialog1
->done());
177 EXPECT_FALSE(test_dialog2
->done());
178 EXPECT_TRUE(web_contents_modal_dialog_manager
->IsShowingDialog());
180 // test_dialog2 will be shown. Focus should be on test_dialog2's text field.
181 EXPECT_EQ(test_dialog2
->GetInitiallyFocusedView(),
182 focus_manager
->GetFocusedView());
184 int tab_with_constrained_window
=
185 browser()->tab_strip_model()->active_index();
188 chrome::NewTab(browser());
190 // The constrained dialog should no longer be selected.
191 EXPECT_NE(test_dialog2
->GetInitiallyFocusedView(),
192 focus_manager
->GetFocusedView());
194 browser()->tab_strip_model()->ActivateTabAt(tab_with_constrained_window
,
197 // Activating the previous tab should bring focus to the constrained window.
198 EXPECT_EQ(test_dialog2
->GetInitiallyFocusedView(),
199 focus_manager
->GetFocusedView());
201 // Send another VKEY_RETURN, closing test_dialog2
202 EXPECT_TRUE(focus_manager
->ProcessAccelerator(
203 ui::Accelerator(ui::VKEY_RETURN
, ui::EF_NONE
)));
204 content::RunAllPendingInMessageLoop();
205 EXPECT_TRUE(test_dialog2
->done());
206 EXPECT_FALSE(web_contents_modal_dialog_manager
->IsShowingDialog());
209 // Tests that the constrained window is closed properly when its tab is
211 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest
, TabCloseTest
) {
212 content::WebContents
* web_contents
=
213 browser()->tab_strip_model()->GetActiveWebContents();
214 ASSERT_TRUE(web_contents
!= NULL
);
215 WebContentsModalDialogManager
* web_contents_modal_dialog_manager
=
216 WebContentsModalDialogManager::FromWebContents(web_contents
);
217 ASSERT_TRUE(web_contents_modal_dialog_manager
!= NULL
);
219 // Create a constrained dialog. It will attach itself to web_contents.
220 scoped_ptr
<TestConstrainedDialog
> test_dialog(new TestConstrainedDialog
);
221 views::Widget
* window
= CreateWebContentsModalDialogViews(
223 web_contents
->GetView()->GetNativeView());
224 web_contents_modal_dialog_manager
->ShowDialog(window
->GetNativeView());
227 browser()->tab_strip_model()->CloseWebContentsAt(
228 browser()->tab_strip_model()->active_index(),
229 TabStripModel::CLOSE_NONE
);
231 content::RunAllPendingInMessageLoop();
232 EXPECT_TRUE(test_dialog
->done());
235 // Tests that the constrained window is hidden when an other tab is selected and
236 // shown when its tab is selected again.
237 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest
, TabSwitchTest
) {
238 content::WebContents
* web_contents
=
239 browser()->tab_strip_model()->GetActiveWebContents();
240 ASSERT_TRUE(web_contents
!= NULL
);
242 // Create a constrained dialog. It will attach itself to web_contents.
243 scoped_ptr
<TestConstrainedDialog
> test_dialog(new TestConstrainedDialog
);
244 views::Widget
* window
= CreateWebContentsModalDialogViews(
246 web_contents
->GetView()->GetNativeView());
247 WebContentsModalDialogManager
* web_contents_modal_dialog_manager
=
248 WebContentsModalDialogManager::FromWebContents(web_contents
);
249 web_contents_modal_dialog_manager
->ShowDialog(window
->GetNativeView());
250 EXPECT_TRUE(window
->IsVisible());
252 // Open a new tab. The constrained window should hide itself.
253 browser()->tab_strip_model()->AppendWebContents(
254 content::WebContents::Create(
255 content::WebContents::CreateParams(browser()->profile())),
257 EXPECT_FALSE(window
->IsVisible());
259 // Close the new tab. The constrained window should show itself again.
261 browser()->tab_strip_model()->CloseWebContentsAt(
262 browser()->tab_strip_model()->active_index(),
263 TabStripModel::CLOSE_NONE
);
265 EXPECT_TRUE(window
->IsVisible());
267 // Close the original tab.
268 browser()->tab_strip_model()->CloseWebContentsAt(
269 browser()->tab_strip_model()->active_index(),
270 TabStripModel::CLOSE_NONE
);
271 content::RunAllPendingInMessageLoop();
272 EXPECT_TRUE(test_dialog
->done());
275 #if defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11))
277 // Forwards the key event which has |key_code| to the renderer.
278 void ForwardKeyEvent(content::RenderViewHost
* host
, ui::KeyboardCode key_code
) {
280 MSG native_key_event
= { NULL
, WM_KEYDOWN
, key_code
, 0 };
281 #elif defined(USE_X11)
283 ui::InitXKeyEventForTesting(
284 ui::ET_KEY_PRESSED
, key_code
, ui::EF_NONE
, &x_event
);
285 XEvent
* native_key_event
= &x_event
;
288 #if defined(USE_AURA)
289 ui::KeyEvent
key(native_key_event
, false);
290 ui::KeyEvent
* native_ui_key_event
= &key
;
291 #elif defined(OS_WIN)
292 MSG native_ui_key_event
= native_key_event
;
295 host
->ForwardKeyboardEvent(
296 content::NativeWebKeyboardEvent(native_ui_key_event
));
299 // Tests that backspace is not processed before it's sent to the web contents.
300 // Flaky on win aura. crbug.com/170331
301 #if defined(OS_WIN) && defined(USE_AURA)
302 #define MAYBE_BackspaceSentToWebContent DISABLED_BackspaceSentToWebContent
304 #define MAYBE_BackspaceSentToWebContent BackspaceSentToWebContent
306 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest
,
307 MAYBE_BackspaceSentToWebContent
) {
308 content::WebContents
* web_contents
=
309 browser()->tab_strip_model()->GetActiveWebContents();
310 ASSERT_TRUE(web_contents
!= NULL
);
312 GURL
new_tab_url(chrome::kChromeUINewTabURL
);
313 ui_test_utils::NavigateToURL(browser(), new_tab_url
);
314 GURL
about_url(chrome::kChromeUIAboutURL
);
315 ui_test_utils::NavigateToURL(browser(), about_url
);
317 ConstrainedWebDialogDelegate
* cwdd
= CreateConstrainedWebDialog(
318 browser()->profile(),
319 new ui::test::TestWebDialogDelegate(about_url
),
323 content::RenderViewHost
* render_view_host
=
324 cwdd
->GetWebContents()->GetRenderViewHost();
325 ForwardKeyEvent(render_view_host
, ui::VKEY_BACK
);
327 // Backspace is not processed as accelerator before it's sent to web contents.
328 EXPECT_EQ(about_url
.spec(), web_contents
->GetURL().spec());
330 content::RunAllPendingInMessageLoop();
332 // Backspace is processed as accelerator after it's sent to web contents.
333 EXPECT_EQ(new_tab_url
.spec(), web_contents
->GetURL().spec());
336 // Fails flakily (once per 10-20 runs) on Win Aura only. http://crbug.com/177482
338 #define MAYBE_EscapeCloseConstrainedWindow DISABLED_EscapeCloseConstrainedWindow
340 #define MAYBE_EscapeCloseConstrainedWindow EscapeCloseConstrainedWindow
343 // Tests that escape closes the constrained window.
344 IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest
,
345 MAYBE_EscapeCloseConstrainedWindow
) {
346 content::WebContents
* web_contents
=
347 browser()->tab_strip_model()->GetActiveWebContents();
348 ASSERT_TRUE(web_contents
!= NULL
);
350 GURL
new_tab_url(chrome::kChromeUINewTabURL
);
351 ui_test_utils::NavigateToURL(browser(), new_tab_url
);
352 ConstrainedWebDialogDelegate
* cwdd
= CreateConstrainedWebDialog(
353 browser()->profile(),
354 new ui::test::TestWebDialogDelegate(new_tab_url
),
358 views::Widget
* widget
=
359 views::Widget::GetWidgetForNativeView(cwdd
->GetNativeDialog());
360 views::test::TestWidgetObserver
observer(widget
);
362 content::RenderViewHost
* render_view_host
=
363 cwdd
->GetWebContents()->GetRenderViewHost();
364 ForwardKeyEvent(render_view_host
, ui::VKEY_ESCAPE
);
366 // Escape is not processed as accelerator before it's sent to web contents.
367 EXPECT_FALSE(observer
.widget_closed());
369 content::RunAllPendingInMessageLoop();
371 // Escape is processed as accelerator after it's sent to web contents.
372 EXPECT_TRUE(observer
.widget_closed());
375 #endif // defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11))