root/chrome/browser/ui/views/constrained_window_views_browsertest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetInitiallyFocusedView
  2. done_
  3. GetInitiallyFocusedView
  4. GetContentsView
  5. GetWidget
  6. GetWidget
  7. DeleteDelegate
  8. Accept
  9. Cancel
  10. GetModalType
  11. done
  12. IN_PROC_BROWSER_TEST_F
  13. IN_PROC_BROWSER_TEST_F
  14. IN_PROC_BROWSER_TEST_F
  15. IN_PROC_BROWSER_TEST_F
  16. ForwardKeyEvent
  17. IN_PROC_BROWSER_TEST_F
  18. IN_PROC_BROWSER_TEST_F

// 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 "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/test/events_test_utils_x11.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(),
      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(),
      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(),
      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(),
      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(),
      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)
  ui::ScopedXI2Event x_event;
  x_event.InitKeyEvent(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
  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))

/* [<][>][^][v][top][bottom][index][help] */