// Copyright (c) 2013 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 <stddef.h>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"
#include "ui/base/ui_base_switches.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gl/test/gl_surface_test_support.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/test/focus_manager_test.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/views_interactive_ui_test_base.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/touchui/touch_selection_controller_impl.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/wm/public/activation_client.h"

#if defined(OS_WIN)
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/win/hwnd_util.h"
#endif

namespace views {
namespace test {

namespace {

// A View that closes the Widget and exits the current message-loop when it
// receives a mouse-release event.
class ExitLoopOnRelease : public View {
 public:
  ExitLoopOnRelease() {}
  ~ExitLoopOnRelease() override {}

 private:
  // View:
  void OnMouseReleased(const ui::MouseEvent& event) override {
    GetWidget()->Close();
    base::MessageLoop::current()->QuitNow();
  }

  DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
};

// A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
class GestureCaptureView : public View {
 public:
  GestureCaptureView() {}
  ~GestureCaptureView() override {}

 private:
  // View:
  void OnGestureEvent(ui::GestureEvent* event) override {
    if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
      GetWidget()->SetCapture(this);
      event->StopPropagation();
    }
  }

  DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
};

// A view that always processes all mouse events.
class MouseView : public View {
 public:
  MouseView()
      : View(),
        entered_(0),
        exited_(0),
        pressed_(0) {
  }
  ~MouseView() override {}

  bool OnMousePressed(const ui::MouseEvent& event) override {
    pressed_++;
    return true;
  }

  void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }

  void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }

  // Return the number of OnMouseEntered calls and reset the counter.
  int EnteredCalls() {
    int i = entered_;
    entered_ = 0;
    return i;
  }

  // Return the number of OnMouseExited calls and reset the counter.
  int ExitedCalls() {
    int i = exited_;
    exited_ = 0;
    return i;
  }

  int pressed() const { return pressed_; }

 private:
  int entered_;
  int exited_;

  int pressed_;

  DISALLOW_COPY_AND_ASSIGN(MouseView);
};

// A View that shows a different widget, sets capture on that widget, and
// initiates a nested message-loop when it receives a mouse-press event.
class NestedLoopCaptureView : public View {
 public:
  explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
  ~NestedLoopCaptureView() override {}

 private:
  // View:
  bool OnMousePressed(const ui::MouseEvent& event) override {
    // Start a nested loop.
    widget_->Show();
    widget_->SetCapture(widget_->GetContentsView());
    EXPECT_TRUE(widget_->HasCapture());

    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);

    base::RunLoop run_loop;
    run_loop.Run();
    return true;
  }

  Widget* widget_;

  DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
};

ui::WindowShowState GetWidgetShowState(const Widget* widget) {
  // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
  // because the former is implemented on all platforms but the latter is not.
  return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN :
      widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED :
      widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED :
      widget->IsActive() ? ui::SHOW_STATE_NORMAL :
                           ui::SHOW_STATE_INACTIVE;
}

// Give the OS an opportunity to process messages for an activation change, when
// there is actually no change expected (e.g. ShowInactive()).
void RunPendingMessagesForActiveStatusChange() {
#if defined(OS_MACOSX)
  // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
  // and made active in two steps, so tests should follow up with a ShowSync()
  // or ActivateSync to ensure a consistent state.
  base::RunLoop().RunUntilIdle();
#endif
  // TODO(tapted): Check for desktop aura widgets.
}

// Activate a widget, and wait for it to become active. On non-desktop Aura
// this is just an activation. For other widgets, it means activating and then
// spinning the run loop until the OS has activated the window.
void ActivateSync(Widget* widget) {
  views::test::WidgetActivationWaiter waiter(widget, true);
  widget->Activate();
  waiter.Wait();
}

// Like for ActivateSync(), wait for a widget to become active, but Show() the
// widget rather than calling Activate().
void ShowSync(Widget* widget) {
  views::test::WidgetActivationWaiter waiter(widget, true);
  widget->Show();
  waiter.Wait();
}

void DeactivateSync(Widget* widget) {
#if defined(OS_MACOSX)
  // Deactivation of a window isn't a concept on Mac: If an application is
  // active and it has any activatable windows, then one of them is always
  // active. But we can simulate deactivation (e.g. as if another application
  // became active) by temporarily making |widget| non-activatable, then
  // activating (and closing) a temporary widget.
  widget->widget_delegate()->set_can_activate(false);
  Widget* stealer = new Widget;
  stealer->Init(Widget::InitParams(Widget::InitParams::TYPE_WINDOW));
  ShowSync(stealer);
  stealer->CloseNow();
  widget->widget_delegate()->set_can_activate(true);
#else
  views::test::WidgetActivationWaiter waiter(widget, false);
  widget->Deactivate();
  waiter.Wait();
#endif
}

#if defined(OS_WIN)
void ActivatePlatformWindow(Widget* widget) {
  ::SetActiveWindow(
      widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
}
#endif

// Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
// the OS a chance to activate a widget. However, for this case, the test
// doesn't expect that to happen, so there is nothing to wait for.
void ShowInactiveSync(Widget* widget) {
  widget->ShowInactive();
  RunPendingMessagesForActiveStatusChange();
}

}  // namespace

class WidgetTestInteractive : public WidgetTest {
 public:
  WidgetTestInteractive() {}
  ~WidgetTestInteractive() override {}

  void SetUp() override {
    // On mus these tests run as part of views::ViewsTestSuite which already
    // does this initialization.
    if (!IsMus()) {
      gl::GLSurfaceTestSupport::InitializeOneOff();
      ui::RegisterPathProvider();
      base::FilePath ui_test_pak_path;
      ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
      ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
    }
    WidgetTest::SetUp();
  }

 protected:
#if defined(USE_AURA)
  static void ShowQuickMenuImmediately(
      TouchSelectionControllerImpl* controller) {
    DCHECK(controller);
    if (controller->quick_menu_timer_.IsRunning()) {
      controller->quick_menu_timer_.Stop();
      controller->QuickMenuTimerFired();
    }
  }
#endif  // defined (USE_AURA)

  Widget* CreateWidget() {
    Widget* widget = CreateNativeDesktopWidget();
    widget->SetBounds(gfx::Rect(0, 0, 200, 200));
    return widget;
  }
};

#if defined(OS_WIN)
// Tests whether activation and focus change works correctly in Windows.
// We test the following:-
// 1. If the active aura window is correctly set when a top level widget is
//    created.
// 2. If the active aura window in widget 1 created above, is set to NULL when
//    another top level widget is created and focused.
// 3. On focusing the native platform window for widget 1, the active aura
//    window for widget 1 should be set and that for widget 2 should reset.
// TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
  // Create widget 1 and expect the active window to be its window.
  View* focusable_view1 = new View;
  focusable_view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  Widget* widget1 = CreateWidget();
  widget1->GetContentsView()->AddChildView(focusable_view1);
  widget1->Show();
  aura::Window* root_window1 = widget1->GetNativeView()->GetRootWindow();
  focusable_view1->RequestFocus();

  EXPECT_TRUE(root_window1 != NULL);
  aura::client::ActivationClient* activation_client1 =
      aura::client::GetActivationClient(root_window1);
  EXPECT_TRUE(activation_client1 != NULL);
  EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());

  // Create widget 2 and expect the active window to be its window.
  View* focusable_view2 = new View;
  Widget* widget2 = CreateWidget();
  widget1->GetContentsView()->AddChildView(focusable_view2);
  widget2->Show();
  aura::Window* root_window2 = widget2->GetNativeView()->GetRootWindow();
  focusable_view2->RequestFocus();
  ActivatePlatformWindow(widget2);

  aura::client::ActivationClient* activation_client2 =
      aura::client::GetActivationClient(root_window2);
  EXPECT_TRUE(activation_client2 != NULL);
  EXPECT_EQ(activation_client2->GetActiveWindow(), widget2->GetNativeView());
  EXPECT_EQ(activation_client1->GetActiveWindow(),
            reinterpret_cast<aura::Window*>(NULL));

  // Now set focus back to widget 1 and expect the active window to be its
  // window.
  focusable_view1->RequestFocus();
  ActivatePlatformWindow(widget1);
  EXPECT_EQ(activation_client2->GetActiveWindow(),
            reinterpret_cast<aura::Window*>(NULL));
  EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());

  widget2->CloseNow();
  widget1->CloseNow();
}
#endif  // defined(OS_WIN)

TEST_F(WidgetTestInteractive, CaptureAutoReset) {
  Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
  View* container = new View;
  toplevel->SetContentsView(container);

  EXPECT_FALSE(toplevel->HasCapture());
  toplevel->SetCapture(NULL);
  EXPECT_TRUE(toplevel->HasCapture());

  // By default, mouse release removes capture.
  gfx::Point click_location(45, 15);
  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                         ui::EF_LEFT_MOUSE_BUTTON);
  toplevel->OnMouseEvent(&release);
  EXPECT_FALSE(toplevel->HasCapture());

  // Now a mouse release shouldn't remove capture.
  toplevel->set_auto_release_capture(false);
  toplevel->SetCapture(NULL);
  EXPECT_TRUE(toplevel->HasCapture());
  toplevel->OnMouseEvent(&release);
  EXPECT_TRUE(toplevel->HasCapture());
  toplevel->ReleaseCapture();
  EXPECT_FALSE(toplevel->HasCapture());

  toplevel->Close();
  RunPendingMessages();
}

TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
  Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
  View* container = new View;
  toplevel->SetContentsView(container);

  View* gesture = new GestureCaptureView;
  gesture->SetBounds(0, 0, 30, 30);
  container->AddChildView(gesture);

  MouseView* mouse = new MouseView;
  mouse->SetBounds(30, 0, 30, 30);
  container->AddChildView(mouse);

  toplevel->SetSize(gfx::Size(100, 100));
  toplevel->Show();

  // Start a gesture on |gesture|.
  ui::GestureEvent tap_down(15, 15, 0, base::TimeTicks(),
                            ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
  ui::GestureEvent end(15, 15, 0, base::TimeTicks(),
                       ui::GestureEventDetails(ui::ET_GESTURE_END));
  toplevel->OnGestureEvent(&tap_down);

  // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
  // will not receive the event.
  gfx::Point click_location(45, 15);

  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                       ui::EF_LEFT_MOUSE_BUTTON);
  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                         ui::EF_LEFT_MOUSE_BUTTON);

  EXPECT_TRUE(toplevel->HasCapture());

  toplevel->OnMouseEvent(&press);
  toplevel->OnMouseEvent(&release);
  EXPECT_EQ(0, mouse->pressed());

  EXPECT_FALSE(toplevel->HasCapture());

  // The end of the gesture should release the capture, and pressing on |mouse|
  // should now reach |mouse|.
  toplevel->OnGestureEvent(&end);
  toplevel->OnMouseEvent(&press);
  toplevel->OnMouseEvent(&release);
  EXPECT_EQ(1, mouse->pressed());

  toplevel->Close();
  RunPendingMessages();
}

// Checks that if a mouse-press triggers a capture on a different widget (which
// consumes the mouse-release event), then the target of the press does not have
// capture.
TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
  // The test creates two widgets: |first| and |second|.
  // The View in |first| makes |second| visible, sets capture on it, and starts
  // a nested loop (like a menu does). The View in |second| terminates the
  // nested loop and closes the widget.
  // The test sends a mouse-press event to |first|, and posts a task to send a
  // release event to |second|, to make sure that the release event is
  // dispatched after the nested loop starts.

  Widget* first = CreateTopLevelFramelessPlatformWidget();
  Widget* second = CreateTopLevelFramelessPlatformWidget();

  View* container = new NestedLoopCaptureView(second);
  first->SetContentsView(container);

  second->SetContentsView(new ExitLoopOnRelease());

  first->SetSize(gfx::Size(100, 100));
  first->Show();

  gfx::Point location(20, 20);
  base::ThreadTaskRunnerHandle::Get()->PostTask(
      FROM_HERE, base::Bind(&Widget::OnMouseEvent, base::Unretained(second),
                            base::Owned(new ui::MouseEvent(
                                ui::ET_MOUSE_RELEASED, location, location,
                                ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                                ui::EF_LEFT_MOUSE_BUTTON))));
  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                       ui::EF_LEFT_MOUSE_BUTTON);
  first->OnMouseEvent(&press);
  EXPECT_FALSE(first->HasCapture());
  first->Close();
  RunPendingMessages();
}

// Tests some grab/ungrab events.
// TODO(estade): can this be enabled now that this is an interactive ui test?
TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
  Widget* toplevel = CreateTopLevelPlatformWidget();
  Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
  Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);

  toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));

  child1->SetBounds(gfx::Rect(10, 10, 300, 300));
  View* view = new MouseView();
  view->SetBounds(0, 0, 300, 300);
  child1->GetRootView()->AddChildView(view);

  child2->SetBounds(gfx::Rect(200, 10, 200, 200));
  view = new MouseView();
  view->SetBounds(0, 0, 200, 200);
  child2->GetRootView()->AddChildView(view);

  toplevel->Show();
  RunPendingMessages();

  // Click on child1
  gfx::Point p1(45, 45);
  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
  toplevel->OnMouseEvent(&pressed);

  EXPECT_TRUE(toplevel->HasCapture());
  EXPECT_TRUE(child1->HasCapture());
  EXPECT_FALSE(child2->HasCapture());

  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
  toplevel->OnMouseEvent(&released);

  EXPECT_FALSE(toplevel->HasCapture());
  EXPECT_FALSE(child1->HasCapture());
  EXPECT_FALSE(child2->HasCapture());

  RunPendingMessages();

  // Click on child2
  gfx::Point p2(315, 45);
  ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
  toplevel->OnMouseEvent(&pressed2);
  EXPECT_TRUE(pressed2.handled());
  EXPECT_TRUE(toplevel->HasCapture());
  EXPECT_TRUE(child2->HasCapture());
  EXPECT_FALSE(child1->HasCapture());

  ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, ui::EventTimeForNow(),
                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
  toplevel->OnMouseEvent(&released2);
  EXPECT_FALSE(toplevel->HasCapture());
  EXPECT_FALSE(child1->HasCapture());
  EXPECT_FALSE(child2->HasCapture());

  toplevel->CloseNow();
}

// Tests mouse move outside of the window into the "resize controller" and back
// will still generate an OnMouseEntered and OnMouseExited event..
TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
  Widget* toplevel = CreateTopLevelPlatformWidget();

  toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));

  MouseView* view = new MouseView();
  view->SetBounds(90, 90, 10, 10);
  toplevel->GetRootView()->AddChildView(view);

  toplevel->Show();
  RunPendingMessages();

  // Move to an outside position.
  gfx::Point p1(200, 200);
  ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(),
                           ui::EF_NONE, ui::EF_NONE);
  toplevel->OnMouseEvent(&moved_out);
  EXPECT_EQ(0, view->EnteredCalls());
  EXPECT_EQ(0, view->ExitedCalls());

  // Move onto the active view.
  gfx::Point p2(95, 95);
  ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(),
                            ui::EF_NONE, ui::EF_NONE);
  toplevel->OnMouseEvent(&moved_over);
  EXPECT_EQ(1, view->EnteredCalls());
  EXPECT_EQ(0, view->ExitedCalls());

  // Move onto the outer resizing border.
  gfx::Point p3(102, 95);
  ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3,
                               ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
  toplevel->OnMouseEvent(&moved_resizer);
  EXPECT_EQ(0, view->EnteredCalls());
  EXPECT_EQ(1, view->ExitedCalls());

  // Move onto the view again.
  toplevel->OnMouseEvent(&moved_over);
  EXPECT_EQ(1, view->EnteredCalls());
  EXPECT_EQ(0, view->ExitedCalls());

  RunPendingMessages();

  toplevel->CloseNow();
}

// Test view focus restoration when a widget is deactivated and re-activated.
TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
  Widget* widget1 = CreateTopLevelPlatformWidget();
  View* view1 = new View;
  view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  widget1->GetContentsView()->AddChildView(view1);

  Widget* widget2 = CreateTopLevelPlatformWidget();
  View* view2a = new View;
  View* view2b = new View;
  view2a->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  view2b->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  widget2->GetContentsView()->AddChildView(view2a);
  widget2->GetContentsView()->AddChildView(view2b);

  ShowSync(widget1);
  EXPECT_TRUE(widget1->IsActive());
  view1->RequestFocus();
  EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());

  ShowSync(widget2);
  EXPECT_TRUE(widget2->IsActive());
  EXPECT_FALSE(widget1->IsActive());
  EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
  view2a->RequestFocus();
  EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
  view2b->RequestFocus();
  EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());

  ActivateSync(widget1);
  EXPECT_TRUE(widget1->IsActive());
  EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
  EXPECT_FALSE(widget2->IsActive());
  EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());

  ActivateSync(widget2);
  EXPECT_TRUE(widget2->IsActive());
  EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
  EXPECT_FALSE(widget1->IsActive());
  EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());

  widget1->CloseNow();
  widget2->CloseNow();
}

#if defined(OS_WIN)

// Test view focus retention when a widget's HWND is disabled and re-enabled.
TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
  Widget* widget = CreateTopLevelFramelessPlatformWidget();
  widget->SetContentsView(new View);
  for (int i = 0; i < 2; ++i) {
    widget->GetContentsView()->AddChildView(new View);
    widget->GetContentsView()->child_at(i)->SetFocusBehavior(
        View::FocusBehavior::ALWAYS);
  }

  widget->Show();
  widget->GetNativeWindow()->GetHost()->Show();
  const HWND hwnd = HWNDForWidget(widget);
  EXPECT_TRUE(::IsWindow(hwnd));
  EXPECT_TRUE(::IsWindowEnabled(hwnd));
  EXPECT_EQ(hwnd, ::GetActiveWindow());

  for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
    SCOPED_TRACE(base::StringPrintf("Child view %d", i));
    View* view = widget->GetContentsView()->child_at(i);

    view->RequestFocus();
    EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
    EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
    EXPECT_FALSE(::IsWindowEnabled(hwnd));

    // Oddly, disabling the HWND leaves it active with the focus unchanged.
    EXPECT_EQ(hwnd, ::GetActiveWindow());
    EXPECT_TRUE(widget->IsActive());
    EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());

    EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
    EXPECT_TRUE(::IsWindowEnabled(hwnd));
    EXPECT_EQ(hwnd, ::GetActiveWindow());
    EXPECT_TRUE(widget->IsActive());
    EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
  }

  widget->CloseNow();
}

// This class subclasses the Widget class to listen for activation change
// notifications and provides accessors to return information as to whether
// the widget is active. We need this to ensure that users of the widget
// class activate the widget only when the underlying window becomes really
// active. Previously we would activate the widget in the WM_NCACTIVATE
// message which is incorrect because APIs like FlashWindowEx flash the
// window caption by sending fake WM_NCACTIVATE messages.
class WidgetActivationTest : public Widget {
 public:
  WidgetActivationTest()
      : active_(false) {}

  ~WidgetActivationTest() override {}

  void OnNativeWidgetActivationChanged(bool active) override {
    active_ = active;
  }

  bool active() const { return active_; }

 private:
  bool active_;

  DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
};

// Tests whether the widget only becomes active when the underlying window
// is really active.
TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
  WidgetActivationTest widget1;
  Widget::InitParams init_params =
      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
  init_params.bounds = gfx::Rect(0, 0, 200, 200);
  widget1.Init(init_params);
  widget1.Show();
  EXPECT_EQ(true, widget1.active());

  WidgetActivationTest widget2;
  init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
  widget2.Init(init_params);
  widget2.Show();
  EXPECT_EQ(true, widget2.active());
  EXPECT_EQ(false, widget1.active());

  HWND win32_native_window1 = HWNDForWidget(&widget1);
  EXPECT_TRUE(::IsWindow(win32_native_window1));

  ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
  EXPECT_EQ(false, widget1.active());
  EXPECT_EQ(true, widget2.active());

  ::SetActiveWindow(win32_native_window1);
  EXPECT_EQ(true, widget1.active());
  EXPECT_EQ(false, widget2.active());
}

// On Windows if we create a fullscreen window on a thread, then it affects the
// way other windows on the thread interact with the taskbar. To workaround
// this we reduce the bounds of a fullscreen window by 1px when it loses
// activation. This test verifies the same.
TEST_F(WidgetTestInteractive, FullscreenBoundsReducedOnActivationLoss) {
  Widget widget1;
  Widget::InitParams params =
      CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.native_widget = new DesktopNativeWidgetAura(&widget1);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget1.Init(params);
  widget1.SetBounds(gfx::Rect(0, 0, 200, 200));
  widget1.Show();

  widget1.Activate();
  RunPendingMessages();
  EXPECT_EQ(::GetActiveWindow(),
            widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget());

  widget1.SetFullscreen(true);
  EXPECT_TRUE(widget1.IsFullscreen());
  // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs.
  // This task is queued when a widget becomes fullscreen.
  RunPendingMessages();
  EXPECT_EQ(::GetActiveWindow(),
            widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
  gfx::Rect fullscreen_bounds = widget1.GetWindowBoundsInScreen();

  Widget widget2;
  params.native_widget = new DesktopNativeWidgetAura(&widget2);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget2.Init(params);
  widget2.SetBounds(gfx::Rect(0, 0, 200, 200));
  widget2.Show();

  widget2.Activate();
  RunPendingMessages();
  EXPECT_EQ(::GetActiveWindow(),
            widget2.GetNativeWindow()->GetHost()->GetAcceleratedWidget());

  gfx::Rect fullscreen_bounds_after_activation_loss =
      widget1.GetWindowBoundsInScreen();

  // After deactivation loss the bounds of the fullscreen widget should be
  // reduced by 1px.
  EXPECT_EQ(fullscreen_bounds.height() -
            fullscreen_bounds_after_activation_loss.height(), 1);

  widget1.Activate();
  RunPendingMessages();
  EXPECT_EQ(::GetActiveWindow(),
            widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget());

  gfx::Rect fullscreen_bounds_after_activate =
      widget1.GetWindowBoundsInScreen();

  // After activation the bounds of the fullscreen widget should be restored.
  EXPECT_EQ(fullscreen_bounds, fullscreen_bounds_after_activate);

  widget1.CloseNow();
  widget2.CloseNow();
}

// Ensure the window rect and client rects are correct with a window that was
// maximized.
TEST_F(WidgetTestInteractive, FullscreenMaximizedWindowBounds) {
  Widget widget;
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.native_widget = new DesktopNativeWidgetAura(&widget);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget.set_frame_type(Widget::FRAME_TYPE_FORCE_CUSTOM);
  widget.Init(params);
  widget.SetBounds(gfx::Rect(0, 0, 200, 200));
  widget.Show();

  widget.Maximize();
  EXPECT_TRUE(widget.IsMaximized());

  widget.SetFullscreen(true);
  EXPECT_TRUE(widget.IsFullscreen());
  EXPECT_FALSE(widget.IsMaximized());
  // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs.
  // This task is queued when a widget becomes fullscreen.
  RunPendingMessages();

  aura::WindowTreeHost* host = widget.GetNativeWindow()->GetHost();
  HWND hwnd = host->GetAcceleratedWidget();

  HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  ASSERT_TRUE(!!monitor);
  MONITORINFO monitor_info;
  monitor_info.cbSize = sizeof(monitor_info);
  ASSERT_TRUE(::GetMonitorInfo(monitor, &monitor_info));

  gfx::Rect monitor_bounds(monitor_info.rcMonitor);
  gfx::Rect window_bounds = widget.GetWindowBoundsInScreen();
  gfx::Rect client_area_bounds = host->GetBounds();

  EXPECT_EQ(window_bounds, monitor_bounds);
  EXPECT_EQ(monitor_bounds, client_area_bounds);

  // Setting not fullscreen should return it to maximized.
  widget.SetFullscreen(false);
  EXPECT_FALSE(widget.IsFullscreen());
  EXPECT_TRUE(widget.IsMaximized());

  client_area_bounds = host->GetBounds();
  EXPECT_TRUE(monitor_bounds.Contains(client_area_bounds));
  EXPECT_NE(monitor_bounds, client_area_bounds);

  widget.CloseNow();
}
#endif  // defined(OS_WIN)

#if !defined(OS_CHROMEOS)
// Provides functionality to create a window modal dialog.
class ModalDialogDelegate : public DialogDelegateView {
 public:
  explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
  ~ModalDialogDelegate() override {}

  // WidgetDelegate overrides.
  ui::ModalType GetModalType() const override { return type_; }

 private:
  ui::ModalType type_;

  DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
};

// Tests whether the focused window is set correctly when a modal window is
// created and destroyed. When it is destroyed it should focus the owner window.
TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
  // Fails on mus due to focus issues. http://crbug.com/611601
  if (IsMus())
    return;

  TestWidgetFocusChangeListener focus_listener;
  WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
  const std::vector<gfx::NativeView>& focus_changes =
      focus_listener.focus_changes();

  // Create a top level widget.
  Widget top_level_widget;
  Widget::InitParams init_params =
      CreateParams(Widget::InitParams::TYPE_WINDOW);
  init_params.show_state = ui::SHOW_STATE_NORMAL;
  gfx::Rect initial_bounds(0, 0, 500, 500);
  init_params.bounds = initial_bounds;
  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl(
      init_params, &top_level_widget, nullptr);
  top_level_widget.Init(init_params);
  ShowSync(&top_level_widget);

  gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
  ASSERT_FALSE(focus_listener.focus_changes().empty());
  EXPECT_EQ(1u, focus_changes.size());
  EXPECT_EQ(top_level_native_view, focus_changes[0]);

  // Create a modal dialog.
  // This instance will be destroyed when the dialog is destroyed.
  ModalDialogDelegate* dialog_delegate =
      new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);

  Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
      dialog_delegate, NULL, top_level_widget.GetNativeView());
  modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));

  // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains
  // active status synchronously, even on Mac.
  modal_dialog_widget->Show();

  gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
  ASSERT_EQ(3u, focus_changes.size());
  EXPECT_EQ(nullptr, focus_changes[1]);
  EXPECT_EQ(modal_native_view, focus_changes[2]);

#if defined(OS_MACOSX)
  // Window modal dialogs on Mac are "sheets", which animate to close before
  // activating their parent widget.
  views::test::WidgetActivationWaiter waiter(&top_level_widget, true);
  modal_dialog_widget->Close();
  waiter.Wait();
#else
  modal_dialog_widget->CloseNow();
#endif

  ASSERT_EQ(5u, focus_changes.size());
  EXPECT_EQ(nullptr, focus_changes[3]);
  EXPECT_EQ(top_level_native_view, focus_changes[4]);

  top_level_widget.CloseNow();
  WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
}

// Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
// was deprecated. It does have application modal windows, but only Ash requests
// those.
#if defined(OS_MACOSX) && !defined(USE_AURA)
#define MAYBE_SystemModalWindowReleasesCapture \
    DISABLED_SystemModalWindowReleasesCapture
#else
#define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
#endif

// Test that when opening a system-modal window, capture is released.
TEST_F(WidgetTestInteractive, MAYBE_SystemModalWindowReleasesCapture) {
  // Crashes on mus due to capture issue. http://crbug.com/611764
  if (IsMus())
    return;

  TestWidgetFocusChangeListener focus_listener;
  WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);

  // Create a top level widget.
  Widget top_level_widget;
  Widget::InitParams init_params =
      CreateParams(Widget::InitParams::TYPE_WINDOW);
  init_params.show_state = ui::SHOW_STATE_NORMAL;
  gfx::Rect initial_bounds(0, 0, 500, 500);
  init_params.bounds = initial_bounds;
  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  init_params.native_widget = CreatePlatformDesktopNativeWidgetImpl(
      init_params, &top_level_widget, nullptr);
  top_level_widget.Init(init_params);
  ShowSync(&top_level_widget);

  ASSERT_FALSE(focus_listener.focus_changes().empty());
  EXPECT_EQ(top_level_widget.GetNativeView(),
            focus_listener.focus_changes().back());

  EXPECT_FALSE(top_level_widget.HasCapture());
  top_level_widget.SetCapture(NULL);
  EXPECT_TRUE(top_level_widget.HasCapture());

  // Create a modal dialog.
  ModalDialogDelegate* dialog_delegate =
      new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);

  Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
      dialog_delegate, NULL, top_level_widget.GetNativeView());
  modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
  ShowSync(modal_dialog_widget);

  EXPECT_FALSE(top_level_widget.HasCapture());

  modal_dialog_widget->CloseNow();
  top_level_widget.CloseNow();
  WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
}

#endif  // !defined(OS_CHROMEOS)

TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
  Widget widget;
  Widget::InitParams init_params =
      CreateParams(Widget::InitParams::TYPE_WINDOW);
  init_params.bounds = gfx::Rect(0, 0, 200, 200);
  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
  init_params.native_widget =
      CreatePlatformDesktopNativeWidgetImpl(init_params, &widget, nullptr);
  widget.Init(init_params);

  widget.Show();
  EXPECT_FALSE(widget.IsActive());
}

#if defined(USE_AURA)
// Test that touch selection quick menu is not activated when opened.
TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
#if defined(OS_WIN)
  views_delegate()->set_use_desktop_native_widgets(true);
#endif  // !defined(OS_WIN)

  Widget* widget = CreateWidget();

  Textfield* textfield = new Textfield;
  textfield->SetBounds(0, 0, 200, 20);
  textfield->SetText(base::ASCIIToUTF16("some text"));
  widget->GetRootView()->AddChildView(textfield);

  widget->Show();
  textfield->RequestFocus();
  textfield->SelectAll(true);
  TextfieldTestApi textfield_test_api(textfield);

  RunPendingMessages();

  ui::test::EventGenerator generator(widget->GetNativeWindow());
  generator.GestureTapAt(gfx::Point(10, 10));
  ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
      textfield_test_api.touch_selection_controller()));

  EXPECT_TRUE(textfield->HasFocus());
  EXPECT_TRUE(widget->IsActive());
  EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
  widget->CloseNow();
}
#endif  // defined(USE_AURA)

TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
  // Times out on mus. See http://crbug.com/612193
  if (IsMus())
    return;

#if defined(OS_WIN)
  views_delegate()->set_use_desktop_native_widgets(true);
#endif  // !defined(OS_WIN)

  // Create first widget and view, activate the widget, and focus the view.
  Widget widget1;
  Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
  params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
  widget1.Init(params1);

  View* view1 = new View();
  view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  widget1.GetRootView()->AddChildView(view1);

  ActivateSync(&widget1);

  FocusManager* focus_manager1 = widget1.GetFocusManager();
  ASSERT_TRUE(focus_manager1);
  focus_manager1->SetFocusedView(view1);
  EXPECT_EQ(view1, focus_manager1->GetFocusedView());

  // Create second widget and view, activate the widget, and focus the view.
  Widget widget2;
  Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
  params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
  widget2.Init(params2);

  View* view2 = new View();
  view2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  widget2.GetRootView()->AddChildView(view2);

  ActivateSync(&widget2);
  EXPECT_TRUE(widget2.IsActive());
  EXPECT_FALSE(widget1.IsActive());

  FocusManager* focus_manager2 = widget2.GetFocusManager();
  ASSERT_TRUE(focus_manager2);
  focus_manager2->SetFocusedView(view2);
  EXPECT_EQ(view2, focus_manager2->GetFocusedView());

  // Disable the first view and make sure it loses focus, but its widget is not
  // activated.
  view1->SetEnabled(false);
  EXPECT_NE(view1, focus_manager1->GetFocusedView());
  EXPECT_FALSE(widget1.IsActive());
  EXPECT_TRUE(widget2.IsActive());
}

TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) {
  Widget* widget = CreateTopLevelPlatformWidget();

  ShowSync(widget);
  EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);

  widget->CloseNow();
}

TEST_F(WidgetTestInteractive, ShowInactive) {
  Widget* widget = CreateTopLevelPlatformWidget();

  ShowInactiveSync(widget);
  EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE);

  widget->CloseNow();
}

TEST_F(WidgetTestInteractive, InactiveBeforeShow) {
  Widget* widget = CreateTopLevelPlatformWidget();

  EXPECT_FALSE(widget->IsActive());
  EXPECT_FALSE(widget->IsVisible());

  ShowSync(widget);

  EXPECT_TRUE(widget->IsActive());
  EXPECT_TRUE(widget->IsVisible());

  widget->CloseNow();
}

TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) {
  // Create 2 widgets to ensure window layering does not change.
  Widget* widget = CreateTopLevelPlatformWidget();
  Widget* widget2 = CreateTopLevelPlatformWidget();

  ShowSync(widget2);
  EXPECT_FALSE(widget->IsActive());
  EXPECT_TRUE(widget2->IsVisible());
  EXPECT_TRUE(widget2->IsActive());

  ShowSync(widget);
  EXPECT_TRUE(widget->IsActive());
  EXPECT_FALSE(widget2->IsActive());

  ShowInactiveSync(widget);
  EXPECT_TRUE(widget->IsActive());
  EXPECT_FALSE(widget2->IsActive());
  EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);

  widget2->CloseNow();
  widget->CloseNow();
}

TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
  Widget* widget = CreateTopLevelPlatformWidget();
  widget->SetBounds(gfx::Rect(100, 100, 100, 100));

  ShowInactiveSync(widget);
  ShowSync(widget);
  EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);

  widget->CloseNow();
}

#if !defined(OS_CHROMEOS)
TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
  Widget* widget = CreateTopLevelPlatformWidget();
  ShowSync(widget);
  EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);

  Widget widget2;
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget =
      CreatePlatformDesktopNativeWidgetImpl(params, &widget2, nullptr);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget2.Init(params);
  widget2.Show();
  RunPendingMessagesForActiveStatusChange();

  EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE);
  EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);

  widget->CloseNow();
  widget2.CloseNow();
}
#endif  // !defined(OS_CHROMEOS)

// ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
// currently only Desktop widgets and fullscreen changes have to coordinate with
// the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
// Maximize on mac is also (intentionally) a no-op.
#if defined(OS_MACOSX) && !defined(USE_AURA)
#define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
#else
#define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
#endif

// Test that window state is not changed after getting out of full screen.
TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
  Widget* toplevel = CreateTopLevelPlatformWidget();

  toplevel->Show();
  RunPendingMessages();

  // This should be a normal state window.
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));

  toplevel->SetFullscreen(true);
  EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
  toplevel->SetFullscreen(false);
  EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));

  // And it should still be in normal state after getting out of full screen.
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));

  // Now, make it maximized.
  toplevel->Maximize();
  EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));

  toplevel->SetFullscreen(true);
  EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
  toplevel->SetFullscreen(false);
  EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));

  // And it stays maximized after getting out of full screen.
  EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));

  // Clean up.
  toplevel->Close();
  RunPendingMessages();
}

// Testing initial focus is assigned properly for normal top-level widgets,
// and subclasses that specify a initially focused child view.
TEST_F(WidgetTestInteractive, InitialFocus) {
  // TODO: test uses GetContext(), which is not applicable to aura-mus.
  // http://crbug.com/663809.
  if (IsAuraMusClient())
    return;

  // By default, there is no initially focused view (even if there is a
  // focusable subview).
  Widget* toplevel(CreateTopLevelPlatformWidget());
  View* view = new View;
  view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  toplevel->GetContentsView()->AddChildView(view);

  ShowSync(toplevel);
  toplevel->Show();
  EXPECT_FALSE(view->HasFocus());
  EXPECT_FALSE(toplevel->GetFocusManager()->GetStoredFocusView());
  toplevel->CloseNow();

  // Testing a widget which specifies a initially focused view.
  TestInitialFocusWidgetDelegate delegate(GetContext());

  Widget* widget = delegate.GetWidget();
  ShowSync(widget);
  widget->Show();
  EXPECT_TRUE(delegate.view()->HasFocus());
  EXPECT_EQ(delegate.view(), widget->GetFocusManager()->GetStoredFocusView());
}

namespace {

// Helper class for CaptureLostTrackingWidget to store whether
// OnMouseCaptureLost has been invoked for a widget.
class CaptureLostState {
 public:
  CaptureLostState() : got_capture_lost_(false) {}

  bool GetAndClearGotCaptureLost() {
    bool value = got_capture_lost_;
    got_capture_lost_ = false;
    return value;
  }

  void OnMouseCaptureLost() { got_capture_lost_ = true; }

 private:
  bool got_capture_lost_;

  DISALLOW_COPY_AND_ASSIGN(CaptureLostState);
};

// Used to verify OnMouseCaptureLost() has been invoked.
class CaptureLostTrackingWidget : public Widget {
 public:
  explicit CaptureLostTrackingWidget(CaptureLostState* capture_lost_state)
      : capture_lost_state_(capture_lost_state) {}

  // Widget:
  void OnMouseCaptureLost() override {
    capture_lost_state_->OnMouseCaptureLost();
    Widget::OnMouseCaptureLost();
  }

 private:
  // Weak. Stores whether OnMouseCaptureLost has been invoked for this widget.
  CaptureLostState* capture_lost_state_;

  DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
};

}  // namespace

class WidgetCaptureTest : public ViewsInteractiveUITestBase {
 public:
  WidgetCaptureTest() {
  }

  ~WidgetCaptureTest() override {}

  void SetUp() override {
    // On mus these tests run as part of views::ViewsTestSuite which already
    // does this initialization.
    if (!IsMus())
      ViewsInteractiveUITestBase::SetUp();
    else
      ViewsTestBase::SetUp();
  }

  // Verifies Widget::SetCapture() results in updating native capture along with
  // invoking the right Widget function.
  void TestCapture(bool use_desktop_native_widget) {
    CaptureLostState capture_state1;
    CaptureLostTrackingWidget widget1(&capture_state1);
    Widget::InitParams params1 =
        CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    params1.native_widget =
        CreateNativeWidget(params1, use_desktop_native_widget, &widget1);
    params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    widget1.Init(params1);
    widget1.Show();

    CaptureLostState capture_state2;
    CaptureLostTrackingWidget widget2(&capture_state2);
    Widget::InitParams params2 =
        CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    params2.native_widget =
        CreateNativeWidget(params2, use_desktop_native_widget, &widget2);
    widget2.Init(params2);
    widget2.Show();

    // Set capture to widget2 and verity it gets it.
    widget2.SetCapture(widget2.GetRootView());
    EXPECT_FALSE(widget1.HasCapture());
    EXPECT_TRUE(widget2.HasCapture());
    EXPECT_FALSE(capture_state1.GetAndClearGotCaptureLost());
    EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost());

    // Set capture to widget1 and verify it gets it.
    widget1.SetCapture(widget1.GetRootView());
    EXPECT_TRUE(widget1.HasCapture());
    EXPECT_FALSE(widget2.HasCapture());
    EXPECT_FALSE(capture_state1.GetAndClearGotCaptureLost());
    EXPECT_TRUE(capture_state2.GetAndClearGotCaptureLost());

    // Release and verify no one has it.
    widget1.ReleaseCapture();
    EXPECT_FALSE(widget1.HasCapture());
    EXPECT_FALSE(widget2.HasCapture());
    EXPECT_TRUE(capture_state1.GetAndClearGotCaptureLost());
    EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost());
  }

  NativeWidget* CreateNativeWidget(const Widget::InitParams& params,
                                   bool create_desktop_native_widget,
                                   Widget* widget) {
    if (create_desktop_native_widget)
      return CreatePlatformDesktopNativeWidgetImpl(params, widget, nullptr);
    return CreatePlatformNativeWidgetImpl(params, widget, kDefault, nullptr);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
};

// See description in TestCapture().
TEST_F(WidgetCaptureTest, Capture) {
  TestCapture(false);
}

// Tests to ensure capture is correctly released from a Widget with capture when
// it is destroyed. Test for crbug.com/622201.
TEST_F(WidgetCaptureTest, DestroyWithCapture_CloseNow) {
  CaptureLostState capture_state;
  CaptureLostTrackingWidget* widget =
      new CaptureLostTrackingWidget(&capture_state);
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.native_widget = CreateNativeWidget(params, true, widget);
  widget->Init(params);
  widget->Show();

  widget->SetCapture(widget->GetRootView());
  EXPECT_TRUE(widget->HasCapture());
  EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
  widget->CloseNow();

  // Fails on mus. http://crbug.com/611764
  if (IsMus())
    EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
  else
    EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost());
}

TEST_F(WidgetCaptureTest, DestroyWithCapture_Close) {
  CaptureLostState capture_state;
  CaptureLostTrackingWidget* widget =
      new CaptureLostTrackingWidget(&capture_state);
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.native_widget = CreateNativeWidget(params, true, widget);
  widget->Init(params);
  widget->Show();

  widget->SetCapture(widget->GetRootView());
  EXPECT_TRUE(widget->HasCapture());
  EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
  widget->Close();
  EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost());
}

TEST_F(WidgetCaptureTest, DestroyWithCapture_WidgetOwnsNativeWidget) {
  Widget widget;
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.native_widget = CreateNativeWidget(params, true, &widget);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget.Init(params);
  widget.Show();

  widget.SetCapture(widget.GetRootView());
  EXPECT_TRUE(widget.HasCapture());
}

#if !defined(OS_CHROMEOS)
// See description in TestCapture(). Creates DesktopNativeWidget.
TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
  // Fails on mus. http://crbug.com/611764
  if (IsMus())
    return;

  TestCapture(true);
}
#endif

// Test that no state is set if capture fails.
TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
  // Fails on mus. http://crbug.com/611764
  if (IsMus())
    return;
  // TODO: test uses GetContext(), which is not applicable to aura-mus.
  // http://crbug.com/663809.
  if (IsAuraMusClient())
    return;

  Widget widget;
  Widget::InitParams params =
      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.bounds = gfx::Rect(400, 400);
  widget.Init(params);

  MouseView* mouse_view1 = new MouseView;
  MouseView* mouse_view2 = new MouseView;
  View* contents_view = new View;
  contents_view->AddChildView(mouse_view1);
  contents_view->AddChildView(mouse_view2);
  widget.SetContentsView(contents_view);

  mouse_view1->SetBounds(0, 0, 200, 400);
  mouse_view2->SetBounds(200, 0, 200, 400);

  // Setting capture should fail because |widget| is not visible.
  widget.SetCapture(mouse_view1);
  EXPECT_FALSE(widget.HasCapture());

  widget.Show();
  ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
  generator.set_current_location(gfx::Point(300, 10));
  generator.PressLeftButton();

  EXPECT_FALSE(mouse_view1->pressed());
  EXPECT_TRUE(mouse_view2->pressed());
}

// Regression test for http://crbug.com/382421 (Linux-Aura issue).
// TODO(pkotwicz): Make test pass on CrOS and Windows.
// TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
#define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
#else
#define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
#endif

// Test that a synthetic mouse exit is sent to the widget which was handling
// mouse events when a different widget grabs capture.
TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
  // Fails on mus. http://crbug.com/611764
  if (IsMus())
    return;

  Widget widget1;
  Widget::InitParams params1 =
      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  params1.native_widget = CreateNativeWidget(params1, true, &widget1);
  params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget1.Init(params1);
  MouseView* mouse_view1 = new MouseView;
  widget1.SetContentsView(mouse_view1);
  widget1.Show();
  widget1.SetBounds(gfx::Rect(300, 300));

  Widget widget2;
  Widget::InitParams params2 =
      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  params2.native_widget = CreateNativeWidget(params2, true, &widget2);
  params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget2.Init(params2);
  widget2.Show();
  widget2.SetBounds(gfx::Rect(400, 0, 300, 300));

  ui::test::EventGenerator generator(widget1.GetNativeWindow());
  generator.set_current_location(gfx::Point(100, 100));
  generator.MoveMouseBy(0, 0);

  EXPECT_EQ(1, mouse_view1->EnteredCalls());
  EXPECT_EQ(0, mouse_view1->ExitedCalls());

  widget2.SetCapture(NULL);
  EXPECT_EQ(0, mouse_view1->EnteredCalls());
  // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
  // in addition to the one generated by Chrome.
  EXPECT_LT(0, mouse_view1->ExitedCalls());
}

namespace {

// Widget observer which grabs capture when the widget is activated.
class CaptureOnActivationObserver : public WidgetObserver {
 public:
  CaptureOnActivationObserver() : activation_observed_(false) {}
  ~CaptureOnActivationObserver() override {}

  // WidgetObserver:
  void OnWidgetActivationChanged(Widget* widget, bool active) override {
    if (active) {
      widget->SetCapture(nullptr);
      activation_observed_ = true;
    }
  }

  bool activation_observed() const { return activation_observed_; }

 private:
  bool activation_observed_;

  DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
};

}  // namespace

// Test that setting capture on widget activation of a non-toplevel widget
// (e.g. a bubble on Linux) succeeds.
TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
  Widget toplevel;
  Widget::InitParams toplevel_params =
      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  toplevel_params.native_widget = CreateNativeWidget(toplevel_params, true,
                                                     &toplevel);
  toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  toplevel.Init(toplevel_params);
  toplevel.Show();

  Widget* child = new Widget;
  Widget::InitParams child_params =
      CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  child_params.parent = toplevel.GetNativeView();
  child_params.context = toplevel.GetNativeWindow();
  child->Init(child_params);

  CaptureOnActivationObserver observer;
  child->AddObserver(&observer);
  child->Show();

#if defined(OS_MACOSX) && !defined(USE_AURA)
  // On Mac, activation is asynchronous. A single trip to the runloop should be
  // sufficient. On Aura platforms, note that since the child widget isn't top-
  // level, the aura window manager gets asked whether the widget is active, not
  // the OS.
  base::RunLoop().RunUntilIdle();
#endif

  EXPECT_TRUE(observer.activation_observed());
  EXPECT_TRUE(child->HasCapture());
}


#if defined(OS_WIN)
namespace {

// Used to verify OnMouseEvent() has been invoked.
class MouseEventTrackingWidget : public Widget {
 public:
  MouseEventTrackingWidget() : got_mouse_event_(false) {}
  ~MouseEventTrackingWidget() override {}

  bool GetAndClearGotMouseEvent() {
    bool value = got_mouse_event_;
    got_mouse_event_ = false;
    return value;
  }

  // Widget:
  void OnMouseEvent(ui::MouseEvent* event) override {
    got_mouse_event_ = true;
    Widget::OnMouseEvent(event);
  }

 private:
  bool got_mouse_event_;

  DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
};

}  // namespace

// Verifies if a mouse event is received on a widget that doesn't have capture
// on Windows that it is correctly processed by the widget that doesn't have
// capture. This behavior is not desired on OSes other than Windows.
TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
  MouseEventTrackingWidget widget1;
  Widget::InitParams params1 =
      CreateParams(views::Widget::InitParams::TYPE_WINDOW);
  params1.native_widget = new DesktopNativeWidgetAura(&widget1);
  params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget1.Init(params1);
  widget1.Show();

  MouseEventTrackingWidget widget2;
  Widget::InitParams params2 =
      CreateParams(views::Widget::InitParams::TYPE_WINDOW);
  params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params2.native_widget = new DesktopNativeWidgetAura(&widget2);
  widget2.Init(params2);
  widget2.Show();

  // Set capture to widget2 and verity it gets it.
  widget2.SetCapture(widget2.GetRootView());
  EXPECT_FALSE(widget1.HasCapture());
  EXPECT_TRUE(widget2.HasCapture());

  widget1.GetAndClearGotMouseEvent();
  widget2.GetAndClearGotMouseEvent();
  // Send a mouse event to the RootWindow associated with |widget1|. Even though
  // |widget2| has capture, |widget1| should still get the event.
  ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
  ui::EventDispatchDetails details = widget1.GetNativeWindow()->
      GetHost()->event_processor()->OnEventFromSource(&mouse_event);
  ASSERT_FALSE(details.dispatcher_destroyed);
  EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
  EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
}
#endif  // defined(OS_WIN)

class WidgetInputMethodInteractiveTest : public WidgetTestInteractive {
 public:
  WidgetInputMethodInteractiveTest() {}

  // testing::Test:
  void SetUp() override {
    WidgetTestInteractive::SetUp();
#if defined(OS_WIN)
    // On Windows, Widget::Deactivate() works by activating the next topmost
    // window on the z-order stack. This only works if there is at least one
    // other window, so make sure that is the case.
    deactivate_widget_ = CreateWidget();
    deactivate_widget_->Show();
#endif
  }

  void TearDown() override {
    if (deactivate_widget_)
      deactivate_widget_->CloseNow();
    WidgetTestInteractive::TearDown();
  }

 private:
  Widget* deactivate_widget_ = nullptr;

  DISALLOW_COPY_AND_ASSIGN(WidgetInputMethodInteractiveTest);
};

// Test input method focus changes affected by top window activaction.
TEST_F(WidgetInputMethodInteractiveTest, Activation) {
  Widget* widget = CreateWidget();
  Textfield* textfield = new Textfield;
  widget->GetRootView()->AddChildView(textfield);
  textfield->RequestFocus();

  ShowSync(widget);

  EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
            widget->GetInputMethod()->GetTextInputType());

  DeactivateSync(widget);

  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            widget->GetInputMethod()->GetTextInputType());
  widget->CloseNow();
}

// Test input method focus changes affected by focus changes within 1 window.
TEST_F(WidgetInputMethodInteractiveTest, OneWindow) {
  Widget* widget = CreateWidget();
  Textfield* textfield1 = new Textfield;
  Textfield* textfield2 = new Textfield;
  textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
  widget->GetRootView()->AddChildView(textfield1);
  widget->GetRootView()->AddChildView(textfield2);

  ShowSync(widget);

  textfield1->RequestFocus();
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
            widget->GetInputMethod()->GetTextInputType());

  textfield2->RequestFocus();
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
            widget->GetInputMethod()->GetTextInputType());

// Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
// DNWA (which just activates the last active window) and involves the
// AuraTestHelper which sets the input method as DummyInputMethod.
#if !defined(OS_CHROMEOS)
  DeactivateSync(widget);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            widget->GetInputMethod()->GetTextInputType());

  ActivateSync(widget);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
            widget->GetInputMethod()->GetTextInputType());

  DeactivateSync(widget);
  textfield1->RequestFocus();
  ActivateSync(widget);
  EXPECT_TRUE(widget->IsActive());
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
            widget->GetInputMethod()->GetTextInputType());
#endif
  widget->CloseNow();
}

// Test input method focus changes affected by focus changes cross 2 windows
// which shares the same top window.
TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) {
  // Fails on mus. http://crbug.com/611766
  if (IsMus())
    return;

  Widget* parent = CreateWidget();
  parent->SetBounds(gfx::Rect(100, 100, 100, 100));

  Widget* child = CreateChildNativeWidgetWithParent(parent);
  child->SetBounds(gfx::Rect(0, 0, 50, 50));
  child->Show();

  Textfield* textfield_parent = new Textfield;
  Textfield* textfield_child = new Textfield;
  textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
  parent->GetRootView()->AddChildView(textfield_parent);
  child->GetRootView()->AddChildView(textfield_child);
  ShowSync(parent);

  EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod());

  textfield_parent->RequestFocus();
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
            parent->GetInputMethod()->GetTextInputType());

  textfield_child->RequestFocus();
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
            parent->GetInputMethod()->GetTextInputType());

// Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
// DNWA (which just activates the last active window) and involves the
// AuraTestHelper which sets the input method as DummyInputMethod.
#if !defined(OS_CHROMEOS)
  DeactivateSync(parent);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            parent->GetInputMethod()->GetTextInputType());

  ActivateSync(parent);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
            parent->GetInputMethod()->GetTextInputType());

  textfield_parent->RequestFocus();
  DeactivateSync(parent);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            parent->GetInputMethod()->GetTextInputType());

  ActivateSync(parent);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
            parent->GetInputMethod()->GetTextInputType());
#endif

  parent->CloseNow();
}

// Test input method focus changes affected by textfield's state changes.
TEST_F(WidgetInputMethodInteractiveTest, TextField) {
  Widget* widget = CreateWidget();
  Textfield* textfield = new Textfield;
  widget->GetRootView()->AddChildView(textfield);
  ShowSync(widget);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            widget->GetInputMethod()->GetTextInputType());

  textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            widget->GetInputMethod()->GetTextInputType());

  textfield->RequestFocus();
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
            widget->GetInputMethod()->GetTextInputType());

  textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
            widget->GetInputMethod()->GetTextInputType());

  textfield->SetReadOnly(true);
  EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
            widget->GetInputMethod()->GetTextInputType());
  widget->CloseNow();
}

// Test input method should not work for accelerator.
TEST_F(WidgetInputMethodInteractiveTest, AcceleratorInTextfield) {
  Widget* widget = CreateWidget();
  Textfield* textfield = new Textfield;
  widget->GetRootView()->AddChildView(textfield);
  ShowSync(widget);
  textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
  textfield->RequestFocus();

  ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
                         ui::VKEY_F, ui::EF_ALT_DOWN);
  ui::Accelerator accelerator(key_event);
  widget->GetFocusManager()->RegisterAccelerator(
      accelerator, ui::AcceleratorManager::kNormalPriority,
      textfield);

  widget->OnKeyEvent(&key_event);
  EXPECT_TRUE(key_event.stopped_propagation());

  widget->GetFocusManager()->UnregisterAccelerators(textfield);

  ui::KeyEvent key_event2(key_event);
  widget->OnKeyEvent(&key_event2);
  EXPECT_FALSE(key_event2.stopped_propagation());

  widget->CloseNow();
}

}  // namespace test
}  // namespace views
