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.
4
5#include "chrome/browser/ui/views/constrained_window_views.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "chrome/browser/ui/browser.h"
9#include "chrome/browser/ui/browser_commands.h"
10#include "chrome/browser/ui/host_desktop.h"
11#include "chrome/browser/ui/tabs/tab_strip_model.h"
12#include "chrome/browser/ui/views/tab_modal_confirm_dialog_views.h"
13#include "chrome/common/url_constants.h"
14#include "chrome/test/base/in_process_browser_test.h"
15#include "chrome/test/base/interactive_test_utils.h"
16#include "chrome/test/base/ui_test_utils.h"
17#include "components/web_modal/web_contents_modal_dialog_host.h"
18#include "components/web_modal/web_contents_modal_dialog_manager.h"
19#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
20#include "content/public/browser/web_contents.h"
21#include "content/public/test/browser_test_utils.h"
22#include "ui/base/accelerators/accelerator.h"
23#include "ui/views/focus/focus_manager.h"
24#include "ui/views/widget/widget.h"
25
26#if defined(OS_WIN)
27#include "base/win/windows_version.h"
28#endif
29
30namespace {
31
32class TestDialog : public views::DialogDelegateView {
33 public:
34  TestDialog() { SetFocusable(true); }
35  virtual ~TestDialog() {}
36
37  virtual views::View* GetInitiallyFocusedView() OVERRIDE { return this; }
38  // Don't delete the delegate yet. Keep it around for inspection later.
39  virtual void DeleteDelegate() OVERRIDE {}
40
41  virtual ui::ModalType GetModalType() const OVERRIDE {
42#if defined(USE_ASH)
43    return ui::MODAL_TYPE_CHILD;
44#else
45    return views::WidgetDelegate::GetModalType();
46#endif
47  }
48
49 private:
50  DISALLOW_COPY_AND_ASSIGN(TestDialog);
51};
52
53// A helper function to create and show a web contents modal dialog.
54scoped_ptr<TestDialog> ShowModalDialog(content::WebContents* web_contents) {
55  scoped_ptr<TestDialog> dialog(new TestDialog());
56  ShowWebModalDialogViews(dialog.get(), web_contents);
57  return dialog.Pass();
58}
59
60} // namespace
61
62typedef InProcessBrowserTest ConstrainedWindowViewTest;
63
64// Tests the intial focus of tab-modal dialogs, the restoration of focus to the
65// browser when they close, and that queued dialogs don't register themselves as
66// accelerator targets until they are displayed.
67IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, FocusTest) {
68  content::WebContents* web_contents =
69      browser()->tab_strip_model()->GetActiveWebContents();
70  EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
71  scoped_ptr<TestDialog> dialog1 = ShowModalDialog(web_contents);
72
73  // |dialog1| should be active and focused.
74  EXPECT_TRUE(dialog1->GetWidget()->IsVisible());
75  views::FocusManager* focus_manager = dialog1->GetWidget()->GetFocusManager();
76  EXPECT_EQ(dialog1->GetContentsView(), focus_manager->GetFocusedView());
77
78  // Create a second dialog. This will also be modal to |web_contents|, but will
79  // remain hidden since the |dialog1| is still showing.
80  scoped_ptr<TestDialog> dialog2 = ShowModalDialog(web_contents);
81  EXPECT_FALSE(dialog2->GetWidget()->IsVisible());
82  EXPECT_TRUE(dialog1->GetWidget()->IsVisible());
83  EXPECT_EQ(focus_manager, dialog2->GetWidget()->GetFocusManager());
84  EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
85  EXPECT_EQ(dialog1->GetContentsView(), focus_manager->GetFocusedView());
86
87  // Pressing return should close |dialog1|.
88  EXPECT_TRUE(focus_manager->ProcessAccelerator(
89      ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)));
90  content::RunAllPendingInMessageLoop();
91  EXPECT_EQ(NULL, dialog1->GetWidget());
92
93  // |dialog2| should be visible and focused.
94  EXPECT_TRUE(dialog2->GetWidget()->IsVisible());
95  EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
96  EXPECT_EQ(dialog2->GetContentsView(), focus_manager->GetFocusedView());
97
98  // Creating a new tab should take focus away from the other tab's dialog.
99  const int tab_with_dialog = browser()->tab_strip_model()->active_index();
100  chrome::NewTab(browser());
101  EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
102  EXPECT_NE(dialog2->GetContentsView(), focus_manager->GetFocusedView());
103
104  // Activating the previous tab should bring focus to the dialog.
105  browser()->tab_strip_model()->ActivateTabAt(tab_with_dialog, false);
106  EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
107  EXPECT_EQ(dialog2->GetContentsView(), focus_manager->GetFocusedView());
108
109  // Pressing enter again should close |dialog2|.
110  EXPECT_TRUE(focus_manager->ProcessAccelerator(
111      ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)));
112  content::RunAllPendingInMessageLoop();
113  EXPECT_EQ(NULL, dialog2->GetWidget());
114  EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_TAB_CONTAINER));
115}
116
117// Tests that the tab-modal window is closed properly when its tab is closed.
118IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabCloseTest) {
119  scoped_ptr<TestDialog> dialog = ShowModalDialog(
120      browser()->tab_strip_model()->GetActiveWebContents());
121  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
122  chrome::CloseTab(browser());
123  content::RunAllPendingInMessageLoop();
124  EXPECT_EQ(NULL, dialog->GetWidget());
125}
126
127// Tests that the tab-modal window is hidden when an other tab is selected and
128// shown when its tab is selected again.
129IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabSwitchTest) {
130  scoped_ptr<TestDialog> dialog = ShowModalDialog(
131      browser()->tab_strip_model()->GetActiveWebContents());
132  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
133
134  // Open a new tab. The tab-modal window should hide itself.
135  chrome::NewTab(browser());
136  EXPECT_FALSE(dialog->GetWidget()->IsVisible());
137
138  // Close the new tab. The tab-modal window should show itself again.
139  chrome::CloseTab(browser());
140  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
141
142  // Close the original tab.
143  chrome::CloseTab(browser());
144  content::RunAllPendingInMessageLoop();
145  EXPECT_EQ(NULL, dialog->GetWidget());
146}
147
148// Tests that tab-modal dialogs follow tabs dragged between browser windows.
149IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabMoveTest) {
150  content::WebContents* web_contents =
151      browser()->tab_strip_model()->GetActiveWebContents();
152  scoped_ptr<TestDialog> dialog = ShowModalDialog(web_contents);
153  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
154
155  // Move the tab to a second browser window; but first create another tab.
156  // That prevents the first browser window from closing when its tab is moved.
157  chrome::NewTab(browser());
158  browser()->tab_strip_model()->DetachWebContentsAt(
159      browser()->tab_strip_model()->GetIndexOfWebContents(web_contents));
160  Browser* browser2 = CreateBrowser(browser()->profile());
161  browser2->tab_strip_model()->AppendWebContents(web_contents, true);
162  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
163
164  // Close the first browser.
165  chrome::CloseWindow(browser());
166  content::RunAllPendingInMessageLoop();
167  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
168
169  // Close the dialog's browser window.
170  chrome::CloseTab(browser2);
171  content::RunAllPendingInMessageLoop();
172  EXPECT_EQ(NULL, dialog->GetWidget());
173}
174
175// Tests that the web contents navigates when backspace is pressed.
176IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, NavigationOnBackspace) {
177  content::WebContents* web_contents =
178      browser()->tab_strip_model()->GetActiveWebContents();
179  content::WaitForLoadStop(web_contents);
180  const GURL original_url = web_contents->GetURL();
181  EXPECT_NE(GURL(chrome::kChromeUIVersionURL), original_url);
182  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL));
183  content::WaitForLoadStop(web_contents);
184  EXPECT_EQ(GURL(chrome::kChromeUIVersionURL), web_contents->GetURL());
185
186  scoped_ptr<TestDialog> dialog = ShowModalDialog(web_contents);
187  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
188  EXPECT_EQ(dialog->GetContentsView(),
189            dialog->GetWidget()->GetFocusManager()->GetFocusedView());
190
191  // Pressing backspace should navigate back and close the dialog.
192  EXPECT_TRUE(chrome::CanGoBack(browser()));
193  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_BACK,
194                                              false, false, false, false));
195  content::RunAllPendingInMessageLoop();
196  content::WaitForLoadStop(web_contents);
197  EXPECT_EQ(NULL, dialog->GetWidget());
198  EXPECT_EQ(original_url, web_contents->GetURL());
199}
200
201// Tests that the dialog closes when the escape key is pressed.
202IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, ClosesOnEscape) {
203#if defined(OS_WIN)
204  // TODO(msw): The widget is not made NULL on XP. http://crbug.com/177482
205  if (base::win::GetVersion() < base::win::VERSION_VISTA)
206    return;
207#endif
208
209  scoped_ptr<TestDialog> dialog = ShowModalDialog(
210      browser()->tab_strip_model()->GetActiveWebContents());
211  EXPECT_TRUE(dialog->GetWidget()->IsVisible());
212  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE,
213                                              false, false, false, false));
214  content::RunAllPendingInMessageLoop();
215  EXPECT_EQ(NULL, dialog->GetWidget());
216}
217