// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/hit_test.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/color/color_provider_manager.h"
#include "ui/color/color_recipe.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
#include "ui/events/event_observer.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/native_theme/native_theme.h"
#include "ui/native_theme/test_native_theme.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/buildflags.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/event_monitor.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/test/mock_drag_controller.h"
#include "ui/views/test/mock_native_widget.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/test_widget_observer.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view_test_api.h"
#include "ui/views/views_test_suite.h"
#include "ui/views/widget/native_widget_delegate.h"
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/widget/widget_deletion_observer.h"
#include "ui/views/widget/widget_interactive_uitest_utils.h"
#include "ui/views/widget/widget_removals_observer.h"
#include "ui/views/widget/widget_utils.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/views/window/native_frame_view.h"

#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/view_prop.h"
#include "ui/views/test/test_platform_native_widget.h"
#include "ui/views/widget/native_widget_aura.h"
#include "ui/wm/core/base_focus_rules.h"
#include "ui/wm/core/focus_controller.h"
#include "ui/wm/core/shadow_controller.h"
#include "ui/wm/core/shadow_controller_delegate.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "ui/base/win/window_event_target.h"
#include "ui/views/win/hwnd_util.h"
#endif

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif

#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif

#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/platform_gl_egl_utility.h"
#endif

namespace views::test {

namespace {

// TODO(tdanderson): This utility function is used in different unittest
//                   files. Move to a common location to avoid
//                   repeated code.
gfx::Point ConvertPointFromWidgetToView(View* view, const gfx::Point& p) {
  gfx::Point tmp(p);
  View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp);
  return tmp;
}

std::unique_ptr<ui::test::EventGenerator> CreateEventGenerator(
    gfx::NativeWindow root_window,
    gfx::NativeWindow target_window) {
  auto generator =
      std::make_unique<ui::test::EventGenerator>(root_window, target_window);
  return generator;
}

class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {
 public:
  explicit TestBubbleDialogDelegateView(View* anchor)
      : BubbleDialogDelegateView(anchor, BubbleBorder::NONE) {}
  ~TestBubbleDialogDelegateView() override = default;

  bool ShouldShowCloseButton() const override {
    reset_controls_called_ = true;
    return true;
  }

  mutable bool reset_controls_called_ = false;
};

// Convenience to make constructing a GestureEvent simpler.
ui::GestureEvent CreateTestGestureEvent(ui::EventType type, int x, int y) {
  return ui::GestureEvent(x, y, 0, base::TimeTicks(),
                          ui::GestureEventDetails(type));
}

ui::GestureEvent CreateTestGestureEvent(const ui::GestureEventDetails& details,
                                        int x,
                                        int y) {
  return ui::GestureEvent(x, y, 0, base::TimeTicks(), details);
}

class TestWidgetRemovalsObserver : public WidgetRemovalsObserver {
 public:
  TestWidgetRemovalsObserver() = default;

  TestWidgetRemovalsObserver(const TestWidgetRemovalsObserver&) = delete;
  TestWidgetRemovalsObserver& operator=(const TestWidgetRemovalsObserver&) =
      delete;

  ~TestWidgetRemovalsObserver() override = default;

  void OnWillRemoveView(Widget* widget, View* view) override {
    removed_views_.insert(view);
  }

  bool DidRemoveView(View* view) {
    return removed_views_.find(view) != removed_views_.end();
  }

 private:
  std::set<View*> removed_views_;
};

}  // namespace

// A view that keeps track of the events it receives, and consumes all scroll
// gesture events and ui::ET_SCROLL events.
class ScrollableEventCountView : public EventCountView {
 public:
  ScrollableEventCountView() = default;

  ScrollableEventCountView(const ScrollableEventCountView&) = delete;
  ScrollableEventCountView& operator=(const ScrollableEventCountView&) = delete;

  ~ScrollableEventCountView() override = default;

 private:
  // Overridden from ui::EventHandler:
  void OnGestureEvent(ui::GestureEvent* event) override {
    EventCountView::OnGestureEvent(event);
    switch (event->type()) {
      case ui::ET_GESTURE_SCROLL_BEGIN:
      case ui::ET_GESTURE_SCROLL_UPDATE:
      case ui::ET_GESTURE_SCROLL_END:
      case ui::ET_SCROLL_FLING_START:
        event->SetHandled();
        break;
      default:
        break;
    }
  }

  void OnScrollEvent(ui::ScrollEvent* event) override {
    EventCountView::OnScrollEvent(event);
    if (event->type() == ui::ET_SCROLL)
      event->SetHandled();
  }
};

// A view that implements GetMinimumSize.
class MinimumSizeFrameView : public NativeFrameView {
 public:
  explicit MinimumSizeFrameView(Widget* frame) : NativeFrameView(frame) {}

  MinimumSizeFrameView(const MinimumSizeFrameView&) = delete;
  MinimumSizeFrameView& operator=(const MinimumSizeFrameView&) = delete;

  ~MinimumSizeFrameView() override = default;

 private:
  // Overridden from View:
  gfx::Size GetMinimumSize() const override { return gfx::Size(300, 400); }
};

// An event handler that simply keeps a count of the different types of events
// it receives.
class EventCountHandler : public ui::EventHandler {
 public:
  EventCountHandler() = default;

  EventCountHandler(const EventCountHandler&) = delete;
  EventCountHandler& operator=(const EventCountHandler&) = delete;

  ~EventCountHandler() override = default;

  int GetEventCount(ui::EventType type) { return event_count_[type]; }

  void ResetCounts() { event_count_.clear(); }

 protected:
  // Overridden from ui::EventHandler:
  void OnEvent(ui::Event* event) override {
    RecordEvent(*event);
    ui::EventHandler::OnEvent(event);
  }

 private:
  void RecordEvent(const ui::Event& event) { ++event_count_[event.type()]; }

  std::map<ui::EventType, int> event_count_;
};

TEST_F(WidgetTest, WidgetInitParams) {
  // Widgets are not transparent by default.
  Widget::InitParams init1;
  EXPECT_EQ(Widget::InitParams::WindowOpacity::kInferred, init1.opacity);
}

// Tests that the internal name is propagated through widget initialization to
// the native widget and back.
class WidgetWithCustomParamsTest : public WidgetTest {
 public:
  using InitFunction = base::RepeatingCallback<void(Widget::InitParams*)>;
  void SetInitFunction(const InitFunction& init) { init_ = std::move(init); }
  Widget::InitParams CreateParams(Widget::InitParams::Type type) override {
    Widget::InitParams params = WidgetTest::CreateParams(type);
    DCHECK(init_) << "If you don't need an init function, use WidgetTest";
    init_.Run(&params);
    return params;
  }

 private:
  InitFunction init_;
};

TEST_F(WidgetWithCustomParamsTest, NamePropagatedFromParams) {
  SetInitFunction(base::BindLambdaForTesting(
      [](Widget::InitParams* params) { params->name = "MyWidget"; }));
  std::unique_ptr<Widget> widget = CreateTestWidget();

  EXPECT_EQ("MyWidget", widget->native_widget_private()->GetName());
  EXPECT_EQ("MyWidget", widget->GetName());
}

TEST_F(WidgetWithCustomParamsTest, NamePropagatedFromDelegate) {
  WidgetDelegate delegate;
  delegate.set_internal_name("Foobar");
  SetInitFunction(base::BindLambdaForTesting(
      [&](Widget::InitParams* params) { params->delegate = &delegate; }));

  std::unique_ptr<Widget> widget = CreateTestWidget();

  EXPECT_EQ(delegate.internal_name(),
            widget->native_widget_private()->GetName());
  EXPECT_EQ(delegate.internal_name(), widget->GetName());
}

TEST_F(WidgetWithCustomParamsTest, NamePropagatedFromContentsViewClassName) {
  class ViewWithClassName : public View {
   public:
    const char* GetClassName() const override { return "ViewWithClassName"; }
  };

  WidgetDelegate delegate;
  auto view = std::make_unique<ViewWithClassName>();
  auto* contents = delegate.SetContentsView(std::move(view));
  SetInitFunction(base::BindLambdaForTesting(
      [&](Widget::InitParams* params) { params->delegate = &delegate; }));

  std::unique_ptr<Widget> widget = CreateTestWidget();

  EXPECT_EQ(contents->GetClassName(),
            widget->native_widget_private()->GetName());
  EXPECT_EQ(contents->GetClassName(), widget->GetName());
}

#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(WidgetWithCustomParamsTest, SkottieColorsTest) {
  struct SkottieColors {
    bool operator==(const SkottieColors& other) const {
      return color1 == other.color1 && color1_shade1 == other.color1_shade1 &&
             color1_shade2 == other.color1_shade2 && color2 == other.color2 &&
             color3 == other.color3 && color4 == other.color4 &&
             color5 == other.color5 && color6 == other.color6 &&
             base_color == other.base_color &&
             secondary_color == other.secondary_color;
    }
    bool operator!=(const SkottieColors& other) const {
      return !operator==(other);
    }

    SkColor color1, color1_shade1, color1_shade2, color2, color3, color4,
        color5, color6, base_color, secondary_color;
  };

  class ViewObservingSkottieColors : public View {
   public:
    void OnThemeChanged() override {
      View::OnThemeChanged();
      const ui::ColorProvider* provider = GetColorProvider();
      history.push_back({provider->GetColor(ui::kColorNativeColor1),
                         provider->GetColor(ui::kColorNativeColor1Shade1),
                         provider->GetColor(ui::kColorNativeColor1Shade2),
                         provider->GetColor(ui::kColorNativeColor2),
                         provider->GetColor(ui::kColorNativeColor3),
                         provider->GetColor(ui::kColorNativeColor4),
                         provider->GetColor(ui::kColorNativeColor5),
                         provider->GetColor(ui::kColorNativeColor6),
                         provider->GetColor(ui::kColorNativeBaseColor),
                         provider->GetColor(ui::kColorNativeSecondaryColor)});
    }

    std::vector<SkottieColors> history;
  };

  // |widget1| has low background elevation and is created in light mode.
  ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi();
  theme->set_use_dark_colors(false);
  WidgetDelegate delegate1;
  ViewObservingSkottieColors* contents1 =
      delegate1.SetContentsView(std::make_unique<ViewObservingSkottieColors>());
  SetInitFunction(base::BindLambdaForTesting([&](Widget::InitParams* params) {
    params->delegate = &delegate1;
    params->background_elevation =
        ui::ColorProviderManager::ElevationMode::kLow;
  }));
  std::unique_ptr<Widget> widget1 = CreateTestWidget();
  ASSERT_EQ(1u, contents1->history.size());

  // |widget2| has high background elevation and is created in light mode.
  WidgetDelegate delegate2;
  ViewObservingSkottieColors* contents2 =
      delegate2.SetContentsView(std::make_unique<ViewObservingSkottieColors>());
  SetInitFunction(base::BindLambdaForTesting([&](Widget::InitParams* params) {
    params->delegate = &delegate2;
    params->background_elevation =
        ui::ColorProviderManager::ElevationMode::kHigh;
  }));
  std::unique_ptr<Widget> widget2 = CreateTestWidget();
  ASSERT_EQ(1u, contents2->history.size());
  // Check that |contents1| and |contents2| have the same Skottie colors.
  // Background elevation should not affect Skottie colors in light mode.
  EXPECT_EQ(contents1->history[0u], contents2->history[0u]);

  // Switch to dark mode.
  theme->set_use_dark_colors(true);
  theme->NotifyOnNativeThemeUpdated();
  // Check that |contents1| and |contents2| were notified of the theme update.
  ASSERT_EQ(2u, contents1->history.size());
  ASSERT_EQ(2u, contents2->history.size());
  // Check that the Skottie colors were actually changed with the notification.
  EXPECT_NE(contents1->history[0u], contents1->history[1u]);
  EXPECT_NE(contents2->history[0u], contents2->history[1u]);
  // Check that |contents1| and |contents2| have different Skottie colors.
  // Background elevation should affect Skottie colors in dark mode.
  EXPECT_NE(contents1->history[1u], contents2->history[1u]);

  // |widget3| has low background elevation and is created in dark mode.
  WidgetDelegate delegate3;
  ViewObservingSkottieColors* contents3 =
      delegate3.SetContentsView(std::make_unique<ViewObservingSkottieColors>());
  SetInitFunction(base::BindLambdaForTesting([&](Widget::InitParams* params) {
    params->delegate = &delegate3;
    params->background_elevation =
        ui::ColorProviderManager::ElevationMode::kLow;
  }));
  std::unique_ptr<Widget> widget3 = CreateTestWidget();
  ASSERT_EQ(1u, contents3->history.size());
  // Check that |contents3| has the same Skottie colors as |contents1|. It
  // should not matter whether a widget was created before or after dark mode
  // was toggled.
  EXPECT_EQ(contents1->history[1u], contents3->history[0u]);

  // |widget4| has high background elevation and is created in dark mode.
  WidgetDelegate delegate4;
  ViewObservingSkottieColors* contents4 =
      delegate4.SetContentsView(std::make_unique<ViewObservingSkottieColors>());
  SetInitFunction(base::BindLambdaForTesting([&](Widget::InitParams* params) {
    params->delegate = &delegate4;
    params->background_elevation =
        ui::ColorProviderManager::ElevationMode::kHigh;
  }));
  std::unique_ptr<Widget> widget4 = CreateTestWidget();
  ASSERT_EQ(1u, contents4->history.size());
  // Check that |contents4| has the same Skottie colors as |contents2|. It
  // should not matter whether a widget was created before or after dark mode
  // was toggled.
  EXPECT_EQ(contents2->history[1u], contents4->history[0u]);

  // Switch to light mode.
  theme->set_use_dark_colors(false);
  theme->NotifyOnNativeThemeUpdated();
  // Check that all four contents views were notified of the theme update.
  ASSERT_EQ(3u, contents1->history.size());
  ASSERT_EQ(3u, contents2->history.size());
  ASSERT_EQ(2u, contents3->history.size());
  ASSERT_EQ(2u, contents4->history.size());
  // Check that |contents1| and |contents2| are back to the Skottie colors they
  // started with. It should not matter if dark mode is toggled on and back off.
  EXPECT_EQ(contents1->history[0u], contents1->history[2u]);
  EXPECT_EQ(contents2->history[0u], contents2->history[2u]);
  // Check that |contents3| and |contents4| still have the same Skottie colors
  // as |contents1| and |contents2|, respectively. It should not matter when a
  // widget was created.
  EXPECT_EQ(contents1->history[2u], contents3->history[1u]);
  EXPECT_EQ(contents2->history[2u], contents4->history[1u]);
}
#endif

class WidgetColorModeTest : public WidgetTest {
 public:
  static constexpr SkColor kLightColor = SK_ColorWHITE;
  static constexpr SkColor kDarkColor = SK_ColorBLACK;

  WidgetColorModeTest() = default;
  ~WidgetColorModeTest() override = default;

  void SetUp() override {
    WidgetTest::SetUp();

    // Setup color provider for the ui::kColorSysPrimary color.
    ui::ColorProviderManager& manager =
        ui::ColorProviderManager::GetForTesting();
    manager.AppendColorProviderInitializer(base::BindRepeating(&AddColor));
  }

  void TearDown() override {
    ui::ColorProviderManager::ResetForTesting();
    WidgetTest::TearDown();
  }

 private:
  static void AddColor(ui::ColorProvider* provider,
                       const ui::ColorProviderManager::Key& key) {
    ui::ColorMixer& mixer = provider->AddMixer();
    mixer[ui::kColorSysPrimary] = {
        key.color_mode == ui::ColorProviderManager::ColorMode::kDark
            ? kDarkColor
            : kLightColor};
  }
};

TEST_F(WidgetColorModeTest, ColorModeOverride_NoOverride) {
  ui::TestNativeTheme test_theme;
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  test_theme.SetDarkMode(true);
  widget->SetNativeThemeForTest(&test_theme);

  widget->SetColorModeOverride({});
  // Verify that we resolve the dark color when we don't override color mode.
  EXPECT_EQ(kDarkColor,
            widget->GetColorProvider()->GetColor(ui::kColorSysPrimary));
}

TEST_F(WidgetColorModeTest, ColorModeOverride_DarkOverride) {
  ui::TestNativeTheme test_theme;
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  test_theme.SetDarkMode(false);
  widget->SetNativeThemeForTest(&test_theme);

  widget->SetColorModeOverride(ui::ColorProviderManager::ColorMode::kDark);
  // Verify that we resolve the light color even though the theme is dark.
  EXPECT_EQ(kDarkColor,
            widget->GetColorProvider()->GetColor(ui::kColorSysPrimary));
}

TEST_F(WidgetColorModeTest, ColorModeOverride_LightOverride) {
  ui::TestNativeTheme test_theme;
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  test_theme.SetDarkMode(true);
  widget->SetNativeThemeForTest(&test_theme);

  widget->SetColorModeOverride(ui::ColorProviderManager::ColorMode::kLight);
  // Verify that we resolve the light color even though the theme is dark.
  EXPECT_EQ(kLightColor,
            widget->GetColorProvider()->GetColor(ui::kColorSysPrimary));
}

TEST_F(WidgetTest, NativeWindowProperty) {
  const char* key = "foo";
  int value = 3;

  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  EXPECT_EQ(nullptr, widget->GetNativeWindowProperty(key));

  widget->SetNativeWindowProperty(key, &value);
  EXPECT_EQ(&value, widget->GetNativeWindowProperty(key));

  widget->SetNativeWindowProperty(key, nullptr);
  EXPECT_EQ(nullptr, widget->GetNativeWindowProperty(key));
}

TEST_F(WidgetTest, GetParent) {
  // Create a hierarchy of native widgets.
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  Widget* child = CreateChildPlatformWidget(toplevel->GetNativeView());
  Widget* grandchild = CreateChildPlatformWidget(child->GetNativeView());

  EXPECT_EQ(nullptr, toplevel->parent());
  EXPECT_EQ(child, grandchild->parent());
  EXPECT_EQ(toplevel.get(), child->parent());

  // children should be automatically destroyed with |toplevel|.
}

// Verify that there is no change in focus if |enable_arrow_key_traversal| is
// false (the default).
TEST_F(WidgetTest, ArrowKeyFocusTraversalOffByDefault) {
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());

  // Establish default value.
  DCHECK(!toplevel->widget_delegate()->enable_arrow_key_traversal());

  View* container = toplevel->client_view();
  container->SetLayoutManager(std::make_unique<FillLayout>());
  auto* const button1 =
      container->AddChildView(std::make_unique<LabelButton>());
  auto* const button2 =
      container->AddChildView(std::make_unique<LabelButton>());
  toplevel->Show();
  button1->RequestFocus();

  ui::KeyEvent right_arrow(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, ui::EF_NONE);
  toplevel->OnKeyEvent(&right_arrow);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());

  ui::KeyEvent left_arrow(ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_NONE);
  toplevel->OnKeyEvent(&left_arrow);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());

  ui::KeyEvent up_arrow(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::EF_NONE);
  toplevel->OnKeyEvent(&up_arrow);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());

  ui::KeyEvent down_arrow(ui::ET_KEY_PRESSED, ui::VKEY_DOWN, ui::EF_NONE);
  toplevel->OnKeyEvent(&down_arrow);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());
}

// Verify that arrow keys can change focus if |enable_arrow_key_traversal| is
// set to true.
TEST_F(WidgetTest, ArrowKeyTraversalMovesFocusBetweenViews) {
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  toplevel->widget_delegate()->SetEnableArrowKeyTraversal(true);

  View* container = toplevel->client_view();
  container->SetLayoutManager(std::make_unique<FillLayout>());
  auto* const button1 =
      container->AddChildView(std::make_unique<LabelButton>());
  auto* const button2 =
      container->AddChildView(std::make_unique<LabelButton>());
  auto* const button3 =
      container->AddChildView(std::make_unique<LabelButton>());
  toplevel->Show();
  button1->RequestFocus();

  // Right should advance focus (similar to TAB).
  ui::KeyEvent right_arrow(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, ui::EF_NONE);
  toplevel->OnKeyEvent(&right_arrow);
  EXPECT_FALSE(button1->HasFocus());
  EXPECT_TRUE(button2->HasFocus());
  EXPECT_FALSE(button3->HasFocus());

  // Down should also advance focus.
  ui::KeyEvent down_arrow(ui::ET_KEY_PRESSED, ui::VKEY_DOWN, ui::EF_NONE);
  toplevel->OnKeyEvent(&down_arrow);
  EXPECT_FALSE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());
  EXPECT_TRUE(button3->HasFocus());

  // Left should reverse focus (similar to SHIFT+TAB).
  ui::KeyEvent left_arrow(ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_NONE);
  toplevel->OnKeyEvent(&left_arrow);
  EXPECT_FALSE(button1->HasFocus());
  EXPECT_TRUE(button2->HasFocus());
  EXPECT_FALSE(button3->HasFocus());

  // Up should also reverse focus.
  ui::KeyEvent up_arrow(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::EF_NONE);
  toplevel->OnKeyEvent(&up_arrow);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());
  EXPECT_FALSE(button3->HasFocus());

  // Test backwards wrap-around.
  ui::KeyEvent up_arrow2(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::EF_NONE);
  toplevel->OnKeyEvent(&up_arrow2);
  EXPECT_FALSE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());
  EXPECT_TRUE(button3->HasFocus());

  // Test forward wrap-around.
  ui::KeyEvent down_arrow2(ui::ET_KEY_PRESSED, ui::VKEY_DOWN, ui::EF_NONE);
  toplevel->OnKeyEvent(&down_arrow2);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());
  EXPECT_FALSE(button3->HasFocus());
}

TEST_F(WidgetTest, ArrowKeyTraversalNotInheritedByChildWidgets) {
  WidgetAutoclosePtr parent(CreateTopLevelPlatformWidget());
  Widget* child = CreateChildPlatformWidget(parent->GetNativeView());

  parent->widget_delegate()->SetEnableArrowKeyTraversal(true);

  View* container = child->GetContentsView();
  DCHECK(container);
  container->SetLayoutManager(std::make_unique<FillLayout>());
  auto* const button1 =
      container->AddChildView(std::make_unique<LabelButton>());
  auto* const button2 =
      container->AddChildView(std::make_unique<LabelButton>());
  parent->Show();
  child->Show();
  button1->RequestFocus();

  // Arrow key should not cause focus change on child since only the parent
  // Widget has |enable_arrow_key_traversal| set.
  ui::KeyEvent right_arrow(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, ui::EF_NONE);
  child->OnKeyEvent(&right_arrow);
  EXPECT_TRUE(button1->HasFocus());
  EXPECT_FALSE(button2->HasFocus());
}

TEST_F(WidgetTest, ArrowKeyTraversalMayBeExplicitlyEnabledByChildWidgets) {
  WidgetAutoclosePtr parent(CreateTopLevelPlatformWidget());
  Widget* child = CreateChildPlatformWidget(parent->GetNativeView());

  child->widget_delegate()->SetEnableArrowKeyTraversal(true);

  View* container = child->GetContentsView();
  container->SetLayoutManager(std::make_unique<FillLayout>());
  auto* const button1 =
      container->AddChildView(std::make_unique<LabelButton>());
  auto* const button2 =
      container->AddChildView(std::make_unique<LabelButton>());
  parent->Show();
  child->Show();
  button1->RequestFocus();

  // Arrow key should cause focus key on child since child has flag set, even
  // if the parent Widget does not.
  ui::KeyEvent right_arrow(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, ui::EF_NONE);
  child->OnKeyEvent(&right_arrow);
  EXPECT_FALSE(button1->HasFocus());
  EXPECT_TRUE(button2->HasFocus());
}

////////////////////////////////////////////////////////////////////////////////
// Widget::GetTopLevelWidget tests.

TEST_F(WidgetTest, GetTopLevelWidget_Native) {
  // Create a hierarchy of native widgets.
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  gfx::NativeView parent = toplevel->GetNativeView();
  Widget* child = CreateChildPlatformWidget(parent);

  EXPECT_EQ(toplevel.get(), toplevel->GetTopLevelWidget());
  EXPECT_EQ(toplevel.get(), child->GetTopLevelWidget());

  // |child| should be automatically destroyed with |toplevel|.
}

// Test if a focus manager and an inputmethod work without CHECK failure
// when window activation changes.
TEST_F(WidgetTest, ChangeActivation) {
  WidgetAutoclosePtr top1(CreateTopLevelPlatformWidget());
  top1->Show();
  RunPendingMessages();

  WidgetAutoclosePtr top2(CreateTopLevelPlatformWidget());
  top2->Show();
  RunPendingMessages();

  top1->Activate();
  RunPendingMessages();

  top2->Activate();
  RunPendingMessages();

  top1->Activate();
  RunPendingMessages();
}

// Tests visibility of child widgets.
TEST_F(WidgetTest, Visibility) {
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  gfx::NativeView parent = toplevel->GetNativeView();
  Widget* child = CreateChildPlatformWidget(parent);

  EXPECT_FALSE(toplevel->IsVisible());
  EXPECT_FALSE(child->IsVisible());

  // Showing a child with a hidden parent keeps the child hidden.
  child->Show();
  EXPECT_FALSE(toplevel->IsVisible());
  EXPECT_FALSE(child->IsVisible());

  // Showing a hidden parent with a visible child shows both.
  toplevel->Show();
  EXPECT_TRUE(toplevel->IsVisible());
  EXPECT_TRUE(child->IsVisible());

  // Hiding a parent hides both parent and child.
  toplevel->Hide();
  EXPECT_FALSE(toplevel->IsVisible());
  EXPECT_FALSE(child->IsVisible());

  // Hiding a child while the parent is hidden keeps the child hidden when the
  // parent is shown.
  child->Hide();
  toplevel->Show();
  EXPECT_TRUE(toplevel->IsVisible());
  EXPECT_FALSE(child->IsVisible());

  // |child| should be automatically destroyed with |toplevel|.
}

// Test that child widgets are positioned relative to their parent.
TEST_F(WidgetTest, ChildBoundsRelativeToParent) {
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  Widget* child = CreateChildPlatformWidget(toplevel->GetNativeView());

  toplevel->SetBounds(gfx::Rect(160, 100, 320, 200));
  child->SetBounds(gfx::Rect(0, 0, 320, 200));

  child->Show();
  toplevel->Show();

  gfx::Rect toplevel_bounds = toplevel->GetWindowBoundsInScreen();

  // Check the parent origin. If it was (0, 0) the test wouldn't be interesting.
  EXPECT_NE(gfx::Vector2d(0, 0), toplevel_bounds.OffsetFromOrigin());

  // The child's origin is at (0, 0), but the same size, so bounds should match.
  EXPECT_EQ(toplevel_bounds, child->GetWindowBoundsInScreen());
}

////////////////////////////////////////////////////////////////////////////////
// Widget ownership tests.
//
// Tests various permutations of Widget ownership specified in the
// InitParams::Ownership param. Make sure that they are properly destructed
// during shutdown.

// A bag of state to monitor destructions.
struct OwnershipTestState {
  OwnershipTestState() = default;

  bool widget_deleted = false;
  bool native_widget_deleted = false;
};

class WidgetOwnershipTest : public WidgetTest {
 public:
  WidgetOwnershipTest() = default;

  WidgetOwnershipTest(const WidgetOwnershipTest&) = delete;
  WidgetOwnershipTest& operator=(const WidgetOwnershipTest&) = delete;

  ~WidgetOwnershipTest() override = default;

  void TearDown() override {
    EXPECT_TRUE(state()->widget_deleted);
    EXPECT_TRUE(state()->native_widget_deleted);
    WidgetTest::TearDown();
  }

  OwnershipTestState* state() { return &state_; }

 private:
  OwnershipTestState state_;
};

// A Widget subclass that updates a bag of state when it is destroyed.
class OwnershipTestWidget : public Widget {
 public:
  explicit OwnershipTestWidget(OwnershipTestState* state) : state_(state) {}

  OwnershipTestWidget(const OwnershipTestWidget&) = delete;
  OwnershipTestWidget& operator=(const OwnershipTestWidget&) = delete;

  ~OwnershipTestWidget() override { state_->widget_deleted = true; }

 private:
  raw_ptr<OwnershipTestState> state_;
};

class NativeWidgetDestroyedWaiter {
 public:
  explicit NativeWidgetDestroyedWaiter(OwnershipTestState* state)
      : state_(state) {}

  base::OnceClosure GetNativeWidgetDestroyedCallback() {
    return base::BindOnce(
        [](OwnershipTestState* state, base::RunLoop* run_loop) {
          state->native_widget_deleted = true;
          run_loop->Quit();
        },
        state_.get(), &run_loop_);
  }

  void Wait() {
    if (!state_->native_widget_deleted)
      run_loop_.Run();
  }

 private:
  base::RunLoop run_loop_;
  raw_ptr<OwnershipTestState> state_;
};

using NativeWidgetOwnsWidgetTest = WidgetOwnershipTest;
// NativeWidget owns its Widget, part 1.1: NativeWidget is a non-desktop
// widget, CloseNow() destroys Widget and NativeWidget synchronously.
TEST_F(NativeWidgetOwnsWidgetTest, NonDesktopWidget_CloseNow) {
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget, kStubCapture, &state()->native_widget_deleted);
  widget->Init(std::move(params));

  widget->CloseNow();

  // Both widget and native widget should be deleted synchronously.
  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}

// NativeWidget owns its Widget, part 1.2: NativeWidget is a non-desktop
// widget, Close() destroys Widget and NativeWidget asynchronously.
TEST_F(NativeWidgetOwnsWidgetTest, NonDesktopWidget_Close) {
  NativeWidgetDestroyedWaiter waiter(state());
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget, kStubCapture, waiter.GetNativeWidgetDestroyedCallback());
  widget->Init(std::move(params));

  widget->Close();
  waiter.Wait();

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}

// NativeWidget owns its Widget, part 1.3: NativeWidget is a desktop
// widget, Close() destroys Widget and NativeWidget asynchronously.
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
TEST_F(NativeWidgetOwnsWidgetTest, DesktopWidget_Close) {
  NativeWidgetDestroyedWaiter waiter(state());
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget = CreatePlatformDesktopNativeWidgetImpl(
      widget, kStubCapture, waiter.GetNativeWidgetDestroyedCallback());
  widget->Init(std::move(params));

  widget->Close();
  waiter.Wait();

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}
#endif

// NativeWidget owns its Widget, part 1.4: NativeWidget is a desktop
// widget. Unlike desktop widget, CloseNow() might destroy Widget and
// NativeWidget asynchronously.
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
TEST_F(NativeWidgetOwnsWidgetTest, DesktopWidget_CloseNow) {
  NativeWidgetDestroyedWaiter waiter(state());
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget = CreatePlatformDesktopNativeWidgetImpl(
      widget, kStubCapture, waiter.GetNativeWidgetDestroyedCallback());
  widget->Init(std::move(params));

  widget->CloseNow();
  waiter.Wait();

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}
#endif

// NativeWidget owns its Widget, part 2.1: NativeWidget is a non-desktop
// widget. CloseNow() the parent should destroy the child.
TEST_F(NativeWidgetOwnsWidgetTest, NonDestkopWidget_CloseNowParent) {
  NativeWidgetDestroyedWaiter waiter(state());
  Widget* toplevel = CreateTopLevelPlatformWidget();
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.parent = toplevel->GetNativeView();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget, kStubCapture, waiter.GetNativeWidgetDestroyedCallback());
  widget->Init(std::move(params));

  // Now destroy the native widget. This is achieved by closing the toplevel.
  toplevel->CloseNow();
  // The NativeWidget won't be deleted until after a return to the message loop
  // so we have to run pending messages before testing the destruction status.
  waiter.Wait();

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}

// NativeWidget owns its Widget, part 2.2: NativeWidget is a desktop
// widget. CloseNow() the parent should destroy the child.
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
TEST_F(NativeWidgetOwnsWidgetTest, DestkopWidget_CloseNowParent) {
  NativeWidgetDestroyedWaiter waiter(state());
  Widget* toplevel = CreateTopLevelPlatformDesktopWidget();
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.parent = toplevel->GetNativeView();
  params.native_widget = CreatePlatformDesktopNativeWidgetImpl(
      widget, kStubCapture, waiter.GetNativeWidgetDestroyedCallback());
  widget->Init(std::move(params));

  // Now destroy the native widget. This is achieved by closing the toplevel.
  toplevel->CloseNow();
  // The NativeWidget won't be deleted until after a return to the message loop
  // so we have to run pending messages before testing the destruction status.
  waiter.Wait();

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}
#endif

// NativeWidget owns its Widget, part 3.1: NativeWidget is a non-desktop
// widget, destroyed out from under it by the OS.
TEST_F(NativeWidgetOwnsWidgetTest, NonDesktopWidget_NativeDestroy) {
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget, kStubCapture, &state()->native_widget_deleted);
  widget->Init(std::move(params));

  // Now simulate a destroy of the platform native widget from the OS:
  SimulateNativeDestroy(widget);

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}

#if BUILDFLAG(ENABLE_DESKTOP_AURA)
// NativeWidget owns its Widget, part 3.2: NativeWidget is a desktop
// widget, destroyed out from under it by the OS.
TEST_F(NativeWidgetOwnsWidgetTest, DesktopWidget_NativeDestroy) {
  NativeWidgetDestroyedWaiter waiter(state());
  Widget* widget = new OwnershipTestWidget(state());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.native_widget = CreatePlatformDesktopNativeWidgetImpl(
      widget, kStubCapture, waiter.GetNativeWidgetDestroyedCallback());
  widget->Init(std::move(params));

  // Now simulate a destroy of the platform native widget from the OS:
  SimulateDesktopNativeDestroy(widget);
  waiter.Wait();

  EXPECT_TRUE(state()->widget_deleted);
  EXPECT_TRUE(state()->native_widget_deleted);
}
#endif

using WidgetOwnsNativeWidgetTest = WidgetOwnershipTest;
// Widget owns its NativeWidget, part 1.
TEST_F(WidgetOwnsNativeWidgetTest, Ownership) {
  auto widget = std::make_unique<OwnershipTestWidget>(state());
  Widget::InitParams params = CreateParamsForTestWidget();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget.get(), kStubCapture, &state()->native_widget_deleted);
  widget->Init(std::move(params));

  // Now delete the Widget, which should delete the NativeWidget.
  widget.reset();

  // TODO(beng): write test for this ownership scenario and the NativeWidget
  //             being deleted out from under the Widget.
}

// Widget owns its NativeWidget, part 2: destroy the parent view.
TEST_F(WidgetOwnsNativeWidgetTest, DestroyParentView) {
  Widget* toplevel = CreateTopLevelPlatformWidget();

  auto widget = std::make_unique<OwnershipTestWidget>(state());
  Widget::InitParams params = CreateParamsForTestWidget();
  params.parent = toplevel->GetNativeView();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget.get(), kStubCapture, &state()->native_widget_deleted);
  widget->Init(std::move(params));

  // Now close the toplevel, which deletes the view hierarchy.
  toplevel->CloseNow();

  RunPendingMessages();

  // This shouldn't delete the widget because it shouldn't be deleted
  // from the native side.
  EXPECT_FALSE(state()->widget_deleted);
  EXPECT_FALSE(state()->native_widget_deleted);
}

// Widget owns its NativeWidget, part 3: has a WidgetDelegateView as contents.
TEST_F(WidgetOwnsNativeWidgetTest, WidgetDelegateView) {
  auto widget = std::make_unique<OwnershipTestWidget>(state());
  Widget::InitParams params = CreateParamsForTestWidget();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget.get(), kStubCapture, &state()->native_widget_deleted);
  params.delegate = new WidgetDelegateView();
  widget->Init(std::move(params));

  // Allow the Widget to go out of scope. There should be no crash or
  // use-after-free.
}

// Widget owns its NativeWidget, part 4: Widget::CloseNow should be idempotent.
TEST_F(WidgetOwnsNativeWidgetTest, IdempotentCloseNow) {
  auto widget = std::make_unique<OwnershipTestWidget>(state());
  Widget::InitParams params = CreateParamsForTestWidget();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget.get(), kStubCapture, &state()->native_widget_deleted);
  widget->Init(std::move(params));

  // Now close the Widget, which should delete the NativeWidget.
  widget->CloseNow();

  RunPendingMessages();

  // Close the widget again should not crash.
  widget->CloseNow();

  RunPendingMessages();
}

// Widget owns its NativeWidget, part 5: Widget::Close should be idempotent.
TEST_F(WidgetOwnsNativeWidgetTest, IdempotentClose) {
  auto widget = std::make_unique<OwnershipTestWidget>(state());
  Widget::InitParams params = CreateParamsForTestWidget();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget.get(), kStubCapture, &state()->native_widget_deleted);
  widget->Init(std::move(params));

  // Now close the Widget, which should delete the NativeWidget.
  widget->Close();

  RunPendingMessages();

  // Close the widget again should not crash.
  widget->Close();

  RunPendingMessages();
}

// Test for CLIENT_OWNS_WIDGET. The client holds a unique_ptr<Widget>.
// The NativeWidget will be destroyed when the platform window is closed.
using ClientOwnsWidgetTest = WidgetOwnershipTest;

TEST_F(ClientOwnsWidgetTest, Ownership) {
  auto widget = std::make_unique<OwnershipTestWidget>(state());
  Widget::InitParams params = CreateParamsForTestWidget();
  params.native_widget = CreatePlatformNativeWidgetImpl(
      widget.get(), kStubCapture, &state()->native_widget_deleted);
  params.ownership = Widget::InitParams::CLIENT_OWNS_WIDGET;
  widget->Init(std::move(params));

  widget->CloseNow();

  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(state()->native_widget_deleted);
}

////////////////////////////////////////////////////////////////////////////////
// Test to verify using various Widget methods doesn't crash when the underlying
// NativeView and NativeWidget is destroyed. Currently, for
// the WIDGET_OWNS_NATIVE_WIDGET ownership pattern, the NativeWidget will not be
// destroyed, but |native_widget_| will still be set to nullptr.

class WidgetWithDestroyedNativeViewOrNativeWidgetTest
    : public ViewsTestBase,
      public testing::WithParamInterface<
          std::tuple<ViewsTestBase::NativeWidgetType,
                     Widget::InitParams::Ownership>> {
 public:
  WidgetWithDestroyedNativeViewOrNativeWidgetTest() = default;

  WidgetWithDestroyedNativeViewOrNativeWidgetTest(
      const WidgetWithDestroyedNativeViewOrNativeWidgetTest&) = delete;
  WidgetWithDestroyedNativeViewOrNativeWidgetTest& operator=(
      const WidgetWithDestroyedNativeViewOrNativeWidgetTest&) = delete;

  ~WidgetWithDestroyedNativeViewOrNativeWidgetTest() override = default;

  // ViewsTestBase:
  void SetUp() override {
    set_native_widget_type(
        std::get<ViewsTestBase::NativeWidgetType>(GetParam()));
    ViewsTestBase::SetUp();
    if (std::get<Widget::InitParams::Ownership>(GetParam()) ==
        Widget::InitParams::CLIENT_OWNS_WIDGET) {
      widget_ = std::make_unique<Widget>();
      Widget::InitParams params = CreateParamsForTestWidget();
      params.ownership = Widget::InitParams::CLIENT_OWNS_WIDGET;
      widget_->Init(std::move(params));
    } else {
      widget_ = CreateTestWidget();
    }
    widget()->Show();
    widget()->native_widget_private()->CloseNow();
    task_environment()->RunUntilIdle();
  }

  Widget* widget() { return widget_.get(); }

  static std::string PrintTestName(
      const ::testing::TestParamInfo<
          WidgetWithDestroyedNativeViewOrNativeWidgetTest::ParamType>& info) {
    std::string test_name;
    switch (std::get<ViewsTestBase::NativeWidgetType>(info.param)) {
      case ViewsTestBase::NativeWidgetType::kDefault:
        test_name += "DefaultNativeWidget";
        break;
      case ViewsTestBase::NativeWidgetType::kDesktop:
        test_name += "DesktopNativeWidget";
        break;
    }
    test_name += "_";
    switch (std::get<Widget::InitParams::Ownership>(info.param)) {
      case Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET:
        test_name += "WidgetOwnsNativeWidget";
        break;
      case Widget::InitParams::CLIENT_OWNS_WIDGET:
        test_name += "ClientOwnsNativeWidget";
        break;
      case Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET:
        // Note: We don't test for this case in
        // WidgetWithDestroyedNativeViewOrNativeWidgetTest.
        test_name += "NativeWidgetOwnsWidget";
        break;
    }
    return test_name;
  }

 private:
  std::unique_ptr<Widget> widget_;
};

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Activate) {
  widget()->Activate();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, AddAndRemoveObserver) {
  // Constructor calls |AddObserver()|
  TestWidgetObserver observer(widget());
  widget()->RemoveObserver(&observer);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       AddAndRemoveRemovalsObserver) {
  TestWidgetRemovalsObserver removals_observer;
  widget()->AddRemovalsObserver(&removals_observer);
  widget()->RemoveRemovalsObserver(&removals_observer);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, AsWidget) {
  widget()->AsWidget();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, CanActivate) {
  widget()->CanActivate();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, CenterWindow) {
  widget()->CenterWindow(gfx::Size());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ClearNativeFocus) {
  widget()->ClearNativeFocus();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ClientView) {
  widget()->client_view();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Close) {
  widget()->Close();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       CloseAllSecondaryWidgets) {
  widget()->CloseAllSecondaryWidgets();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, CloseNow) {
  widget()->CloseNow();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ClosedReason) {
  widget()->closed_reason();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, CloseWithReason) {
  widget()->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       CreateNonClientFrameView) {
  widget()->CreateNonClientFrameView();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Deactivate) {
  widget()->Deactivate();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, DebugToggleFrameType) {
  widget()->DebugToggleFrameType();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, DraggedView) {
  widget()->dragged_view();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, EndMoveLoop) {
  widget()->EndMoveLoop();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ExecuteCommand) {
  widget()->ExecuteCommand(0);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, FlashFrame) {
  widget()->FlashFrame(true);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, FrameType) {
  widget()->frame_type();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, FrameTypeChanged) {
  widget()->FrameTypeChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetAccelerator) {
  ui::Accelerator accelerator;
  widget()->GetAccelerator(0, &accelerator);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetAllChildWidgets) {
  views::Widget::Widgets widgets;
  Widget::GetAllChildWidgets(widget()->GetNativeView(), &widgets);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetAllOwnedWidgets) {
  views::Widget::Widgets widgets;
  Widget::GetAllOwnedWidgets(widget()->GetNativeView(), &widgets);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetAndSetZOrderLevel) {
  widget()->SetZOrderLevel(ui::ZOrderLevel::kNormal);
  widget()->GetZOrderLevel();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetClientAreaBoundsInScreen) {
  widget()->GetClientAreaBoundsInScreen();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetColorProvider) {
  widget()->GetColorProvider();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetCompositor) {
  widget()->GetCompositor();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetContentsView) {
  widget()->GetContentsView();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetCustomTheme) {
  widget()->GetCustomTheme();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetEventSink) {
  widget()->GetEventSink();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetFocusSearch) {
  widget()->GetFocusSearch();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetFocusManager) {
  widget()->GetFocusManager();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetFocusTraversable) {
  widget()->GetFocusTraversable();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetGestureConsumer) {
  widget()->GetGestureConsumer();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetGestureRecognizer) {
  widget()->GetGestureRecognizer();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetHitTestMask) {
  SkPath mask;
  widget()->GetHitTestMask(&mask);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetInputMethod) {
  widget()->GetInputMethod();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetLayer) {
  widget()->GetLayer();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetMinimumSize) {
  widget()->GetMinimumSize();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetMaximumSize) {
  widget()->GetMaximumSize();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetName) {
  widget()->GetName();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetNativeTheme) {
  widget()->GetNativeTheme();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetNativeView) {
  widget()->GetNativeView();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetNativeWindow) {
  widget()->GetNativeWindow();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetNativeWindowProperty) {
  widget()->GetNativeWindowProperty("xx");
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetNonClientComponent) {
  gfx::Point point;
  widget()->GetNonClientComponent(point);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetPrimaryWindowWidget) {
  widget()->GetPrimaryWindowWidget();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetRestoredBounds) {
  widget()->GetRestoredBounds();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetRootView) {
  widget()->GetRootView();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetSublevelManager) {
  widget()->GetSublevelManager();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetThemeProvider) {
  widget()->GetThemeProvider();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetTooltipManager) {
  widget()->GetTooltipManager();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetTopLevelWidget) {
  widget()->GetTopLevelWidget();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetTopLevelWidgetForNativeView) {
  Widget::GetTopLevelWidgetForNativeView(widget()->GetNativeView());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetWeakPtr) {
  widget()->GetWeakPtr();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetWidgetForNativeView) {
  Widget::GetWidgetForNativeView(widget()->GetNativeView());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetWidgetForNativeWindow) {
  Widget::GetWidgetForNativeWindow(widget()->GetNativeWindow());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetWindowBoundsInScreen) {
  widget()->GetWindowBoundsInScreen();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       GetWorkAreaBoundsInScreen) {
  widget()->GetWorkAreaBoundsInScreen();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetWorkspace) {
  widget()->GetWorkspace();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, GetZOrderSublevel) {
  widget()->GetZOrderSublevel();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, HasCapture) {
  widget()->HasCapture();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, HasFocusManager) {
  widget()->HasFocusManager();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, HasHitTestMask) {
  widget()->HasHitTestMask();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, HasObserver) {
  TestWidgetObserver observer(widget());
  widget()->HasObserver(&observer);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, HasRemovalsObserver) {
  TestWidgetRemovalsObserver observer;
  widget()->HasRemovalsObserver(&observer);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Hide) {
  widget()->Hide();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Init) {
  Widget::InitParams params;
  EXPECT_DCHECK_DEATH(widget()->Init(std::move(params)));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, is_secondary_widget) {
  widget()->is_secondary_widget();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsActive) {
  widget()->IsActive();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsClosed) {
  widget()->IsClosed();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsDialogBox) {
  widget()->IsDialogBox();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsFullscreen) {
  widget()->IsFullscreen();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsMaximized) {
  widget()->IsMaximized();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsMinimized) {
  widget()->IsMinimized();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsModal) {
  widget()->IsModal();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsMouseEventsEnabled) {
  widget()->IsMouseEventsEnabled();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsMoveLoopSupported) {
  widget()->IsMoveLoopSupported();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       IsNativeWidgetInitialized) {
  widget()->IsNativeWidgetInitialized();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsStackedAbove) {
  std::unique_ptr<Widget> other_widget = CreateTestWidget();
  widget()->IsStackedAbove(other_widget->GetNativeView());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       IsTranslucentWindowOpacitySupported) {
  widget()->IsTranslucentWindowOpacitySupported();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, IsVisible) {
  widget()->IsVisible();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       IsVisibleOnAllWorkspaces) {
  widget()->IsVisibleOnAllWorkspaces();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnGestureEvent) {
  ui::GestureEvent event =
      CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN, 5, 5);
  widget()->OnGestureEvent(&event);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnKeyEvent) {
  ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, ui::EF_NONE);
  widget()->OnKeyEvent(&event);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnMouseCaptureLost) {
  widget()->OnMouseCaptureLost();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnMouseEvent) {
  gfx::Point p(200, 200);
  ui::MouseEvent event(ui::ET_MOUSE_MOVED, p, p, ui::EventTimeForNow(),
                       ui::EF_NONE, ui::EF_NONE);
  widget()->OnMouseEvent(&event);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnNativeBlur) {
  widget()->OnNativeBlur();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnNativeFocus) {
  widget()->OnNativeFocus();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnNativeThemeUpdated) {
  ui::TestNativeTheme theme;
  widget()->OnNativeThemeUpdated(&theme);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetActivationChanged) {
  widget()->OnNativeWidgetActivationChanged(false);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetAddedToCompositor) {
  widget()->OnNativeWidgetAddedToCompositor();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetBeginUserBoundsChange) {
  widget()->OnNativeWidgetBeginUserBoundsChange();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnNativeWidgetCreated) {
  EXPECT_DCHECK_DEATH(widget()->OnNativeWidgetCreated());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetDestroyed) {
  widget()->OnNativeWidgetDestroyed();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetDestroying) {
  EXPECT_DCHECK_DEATH(widget()->OnNativeWidgetDestroying());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetEndUserBoundsChange) {
  widget()->OnNativeWidgetEndUserBoundsChange();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnNativeWidgetMove) {
  widget()->OnNativeWidgetMove();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnNativeWidgetPaint) {
  auto display_list = base::MakeRefCounted<cc::DisplayItemList>();
  widget()->OnNativeWidgetPaint(
      ui::PaintContext(display_list.get(), 1, gfx::Rect(), false));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetParentChanged) {
  widget()->OnNativeWidgetParentChanged(nullptr);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetRemovingFromCompositor) {
  widget()->OnNativeWidgetRemovingFromCompositor();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetSizeChanged) {
  widget()->OnNativeWidgetSizeChanged(gfx::Size());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetVisibilityChanged) {
  widget()->OnNativeWidgetVisibilityChanged(false);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetWindowShowStateChanged) {
  widget()->OnNativeWidgetWindowShowStateChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnNativeWidgetWorkspaceChanged) {
  widget()->OnNativeWidgetWorkspaceChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnOwnerClosing) {
  widget()->OnOwnerClosing();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnParentShouldPaintAsActiveChanged) {
  widget()->OnParentShouldPaintAsActiveChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, OnScrollEvent) {
  ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(65, 5),
                         ui::EventTimeForNow(), 0, 0, 20, 0, 20, 2);
  widget()->OnScrollEvent(&scroll);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       OnSizeConstraintsChanged) {
  widget()->OnSizeConstraintsChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, LayerTreeChanged) {
  widget()->LayerTreeChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       LayoutRootViewIfNecessary) {
  widget()->LayoutRootViewIfNecessary();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, LockPaintAsActive) {
  widget()->LockPaintAsActive();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Maximize) {
  widget()->Maximize();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Minimize) {
  widget()->Minimize();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, movement_disabled) {
  widget()->movement_disabled();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, native_widget_private) {
  widget()->native_widget_private();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, native_widget) {
  widget()->native_widget();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, non_client_view) {
  widget()->non_client_view();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       NotifyNativeViewHierarchyChanged) {
  widget()->NotifyNativeViewHierarchyChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       NotifyNativeViewHierarchyWillChange) {
  widget()->NotifyNativeViewHierarchyWillChange();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, NotifyWillRemoveView) {
  widget()->NotifyWillRemoveView(widget()->non_client_view());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, parent) {
  widget()->parent();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       RegisterPaintAsActiveChangedCallback) {
  auto subscription =
      widget()->RegisterPaintAsActiveChangedCallback(base::DoNothing());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ReleaseCapture) {
  widget()->ReleaseCapture();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ReorderNativeViews) {
  widget()->ReorderNativeViews();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ReparentNativeView) {
  EXPECT_DCHECK_DEATH(
      Widget::ReparentNativeView(widget()->GetNativeView(), nullptr));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Restore) {
  widget()->Restore();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, RunMoveLoop) {
  widget()->RunMoveLoop(gfx::Vector2d(), views::Widget::MoveLoopSource::kMouse,
                        views::Widget::MoveLoopEscapeBehavior::kHide);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, RunShellDrag) {
  std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>());
  widget()->RunShellDrag(nullptr, std::move(data), gfx::Point(), 0,
                         ui::mojom::DragEventSource::kMouse);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ScheduleLayout) {
  widget()->ScheduleLayout();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SchedulePaintInRect) {
  widget()->SchedulePaintInRect(gfx::Rect(0, 0, 1, 2));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetAspectRatio) {
  widget()->SetAspectRatio(gfx::SizeF(1.0, 1.0));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetBounds) {
  widget()->SetBounds(gfx::Rect(0, 0, 100, 80));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetBoundsConstrained) {
  widget()->SetBoundsConstrained(gfx::Rect(0, 0, 120, 140));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetCanAppearInExistingFullscreenSpaces) {
  widget()->SetCanAppearInExistingFullscreenSpaces(false);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetCapture) {
  widget()->SetCapture(widget()->GetRootView());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetContentsView) {
  View view;
  EXPECT_DCHECK_DEATH(widget()->SetContentsView(std::make_unique<View>()));
  EXPECT_DCHECK_DEATH(widget()->SetContentsView(&view));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetCursor) {
  widget()->SetCursor(ui::Cursor());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetFocusTraversableParent) {
  std::unique_ptr<Widget> another_widget = CreateTestWidget();
  widget()->SetFocusTraversableParent(another_widget->GetFocusTraversable());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetFocusTraversableParentView) {
  std::unique_ptr<Widget> another_widget = CreateTestWidget();
  widget()->SetFocusTraversableParentView(another_widget->GetContentsView());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetFullscreen) {
  widget()->SetFullscreen(true);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetInitialFocus) {
  widget()->SetInitialFocus(ui::SHOW_STATE_INACTIVE);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetNativeWindowProperty) {
  widget()->SetNativeWindowProperty("xx", widget());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetOpacity) {
  widget()->SetOpacity(0.f);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetShape) {
  auto rects = std::make_unique<Widget::ShapeRects>();
  rects->emplace_back(40, 0, 20, 100);
  rects->emplace_back(0, 40, 100, 20);
  widget()->SetShape(std::move(rects));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetSize) {
  widget()->SetSize(gfx::Size(10, 11));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetVisibilityChangedAnimationsEnabled) {
  widget()->SetVisibilityChangedAnimationsEnabled(false);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetVisibilityAnimationDuration) {
  widget()->SetVisibilityAnimationDuration(base::Seconds(1));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetVisibilityAnimationTransition) {
  widget()->SetVisibilityAnimationTransition(Widget::ANIMATE_BOTH);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, SetVisible) {
  widget()->SetVisibilityAnimationTransition(Widget::ANIMATE_BOTH);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SetVisibleOnAllWorkspaces) {
  widget()->SetVisibleOnAllWorkspaces(true);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       ShouldDescendIntoChildForEventHandling) {
  widget()->ShouldDescendIntoChildForEventHandling(nullptr, gfx::NativeView(),
                                                   nullptr, gfx::Point(0, 0));
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       ShouldHandleNativeWidgetActivationChanged) {
  widget()->ShouldHandleNativeWidgetActivationChanged(true);
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ShouldPaintAsActive) {
  widget()->ShouldPaintAsActive();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ShouldUseNativeFrame) {
  widget()->ShouldUseNativeFrame();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       ShouldWindowContentsBeTransparent) {
  widget()->ShouldWindowContentsBeTransparent();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, Show) {
  widget()->Show();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ShowEmojiPanel) {
  widget()->ShowEmojiPanel();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ShowInactive) {
  widget()->ShowInactive();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, StackAbove) {
  std::unique_ptr<Widget> another_widget = CreateTestWidget();
  widget()->StackAbove(another_widget->GetNativeView());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, StackAboveWidget) {
  std::unique_ptr<Widget> another_widget = CreateTestWidget();
  widget()->StackAboveWidget(another_widget.get());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, StackAtTop) {
  widget()->StackAtTop();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest,
       SynthesizeMouseMoveEvent) {
  widget()->SynthesizeMouseMoveEvent();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ThemeChanged) {
  widget()->ThemeChanged();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, UnlockPaintAsActive) {
  // UnlockPaintAsActive() is called in the destructor of PaintAsActiveLock.
  // External invocation is not allowed.
  EXPECT_DCHECK_DEATH(widget()->UnlockPaintAsActive());
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, UpdateWindowIcon) {
  widget()->UpdateWindowIcon();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, UpdateWindowTitle) {
  widget()->UpdateWindowTitle();
}

TEST_P(WidgetWithDestroyedNativeViewOrNativeWidgetTest, ViewHierarchyChanged) {
  widget()->ViewHierarchyChanged(
      ViewHierarchyChangedDetails(true, nullptr, nullptr, nullptr));
}

INSTANTIATE_TEST_SUITE_P(
    PlatformWidgetWithDestroyedNativeViewOrNativeWidgetTest,
    WidgetWithDestroyedNativeViewOrNativeWidgetTest,
    ::testing::Combine(
        ::testing::Values(ViewsTestBase::NativeWidgetType::kDefault,
                          ViewsTestBase::NativeWidgetType::kDesktop),
        ::testing::Values(Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
                          Widget::InitParams::CLIENT_OWNS_WIDGET)),
    WidgetWithDestroyedNativeViewOrNativeWidgetTest::PrintTestName);

////////////////////////////////////////////////////////////////////////////////
// Widget observer tests.
//

class WidgetObserverTest : public WidgetTest, public WidgetObserver {
 public:
  WidgetObserverTest() = default;
  ~WidgetObserverTest() override = default;

  // Set a widget to Close() the next time the Widget being observed is hidden.
  void CloseOnNextHide(Widget* widget) { widget_to_close_on_hide_ = widget; }

  // Overridden from WidgetObserver:
  void OnWidgetDestroying(Widget* widget) override {
    if (active_ == widget)
      active_ = nullptr;
    if (widget_activated_ == widget)
      widget_activated_ = nullptr;
    widget_closed_ = widget;
  }

  void OnWidgetActivationChanged(Widget* widget, bool active) override {
    if (active) {
      if (widget_activated_)
        widget_activated_->Deactivate();
      widget_activated_ = widget;
      active_ = widget;
    } else {
      if (widget_activated_ == widget)
        widget_activated_ = nullptr;
      widget_deactivated_ = widget;
    }
  }

  void OnWidgetVisibilityChanged(Widget* widget, bool visible) override {
    if (visible) {
      widget_shown_ = widget;
      return;
    }
    widget_hidden_ = widget;
    if (widget_to_close_on_hide_) {
      widget_to_close_on_hide_->Close();
      widget_to_close_on_hide_ = nullptr;
    }
  }

  void OnWidgetBoundsChanged(Widget* widget,
                             const gfx::Rect& new_bounds) override {
    widget_bounds_changed_ = widget;
  }

  void reset() {
    active_ = nullptr;
    widget_closed_ = nullptr;
    widget_activated_ = nullptr;
    widget_deactivated_ = nullptr;
    widget_shown_ = nullptr;
    widget_hidden_ = nullptr;
    widget_bounds_changed_ = nullptr;
  }

  Widget* NewWidget() {
    Widget* widget = CreateTopLevelNativeWidget();
    widget->AddObserver(this);
    return widget;
  }

  const Widget* active() const { return active_; }
  const Widget* widget_closed() const { return widget_closed_; }
  const Widget* widget_activated() const { return widget_activated_; }
  const Widget* widget_deactivated() const { return widget_deactivated_; }
  const Widget* widget_shown() const { return widget_shown_; }
  const Widget* widget_hidden() const { return widget_hidden_; }
  const Widget* widget_bounds_changed() const { return widget_bounds_changed_; }

 private:
  raw_ptr<Widget> active_ = nullptr;

  raw_ptr<Widget> widget_closed_ = nullptr;
  raw_ptr<Widget> widget_activated_ = nullptr;
  raw_ptr<Widget> widget_deactivated_ = nullptr;
  raw_ptr<Widget> widget_shown_ = nullptr;
  raw_ptr<Widget> widget_hidden_ = nullptr;
  raw_ptr<Widget> widget_bounds_changed_ = nullptr;

  raw_ptr<Widget> widget_to_close_on_hide_ = nullptr;
};

// This test appears to be flaky on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_ActivationChange DISABLED_ActivationChange
#else
#define MAYBE_ActivationChange ActivationChange
#endif

TEST_F(WidgetObserverTest, MAYBE_ActivationChange) {
  WidgetAutoclosePtr toplevel1(NewWidget());
  WidgetAutoclosePtr toplevel2(NewWidget());

  toplevel1->Show();
  toplevel2->Show();
  reset();

  toplevel1->Activate();
  RunPendingMessages();
  EXPECT_EQ(toplevel1.get(), widget_activated());

  toplevel2->Activate();
  RunPendingMessages();
  EXPECT_EQ(toplevel1.get(), widget_deactivated());
  EXPECT_EQ(toplevel2.get(), widget_activated());
  EXPECT_EQ(toplevel2.get(), active());
}

namespace {

// This class simulates a focus manager that moves focus to a second widget when
// the first one is closed. It simulates a situation where a sequence of widget
// observers might try to call Widget::Close in response to a OnWidgetClosing().
class WidgetActivationForwarder : public TestWidgetObserver {
 public:
  WidgetActivationForwarder(Widget* current_active_widget,
                            Widget* widget_to_activate)
      : TestWidgetObserver(current_active_widget),
        widget_to_activate_(widget_to_activate) {}

  WidgetActivationForwarder(const WidgetActivationForwarder&) = delete;
  WidgetActivationForwarder& operator=(const WidgetActivationForwarder&) =
      delete;

  ~WidgetActivationForwarder() override = default;

 private:
  // WidgetObserver overrides:
  void OnWidgetClosing(Widget* widget) override {
    widget->OnNativeWidgetActivationChanged(false);
    widget_to_activate_->Activate();
  }
  void OnWidgetActivationChanged(Widget* widget, bool active) override {
    if (!active)
      widget->Close();
  }

  raw_ptr<Widget> widget_to_activate_;
};

// This class observes a widget and counts the number of times OnWidgetClosing
// is called.
class WidgetCloseCounter : public TestWidgetObserver {
 public:
  explicit WidgetCloseCounter(Widget* widget) : TestWidgetObserver(widget) {}

  WidgetCloseCounter(const WidgetCloseCounter&) = delete;
  WidgetCloseCounter& operator=(const WidgetCloseCounter&) = delete;

  ~WidgetCloseCounter() override = default;

  int close_count() const { return close_count_; }

 private:
  // WidgetObserver overrides:
  void OnWidgetClosing(Widget* widget) override { close_count_++; }

  int close_count_ = 0;
};

}  // namespace

// Makes sure close notifications aren't sent more than once when a Widget is
// shutting down. Test for crbug.com/714334
TEST_F(WidgetObserverTest, CloseReentrancy) {
  Widget* widget1 = CreateTopLevelPlatformWidget();
  Widget* widget2 = CreateTopLevelPlatformWidget();
  WidgetCloseCounter counter(widget1);
  WidgetActivationForwarder focus_manager(widget1, widget2);
  widget1->Close();
  EXPECT_EQ(1, counter.close_count());
  widget2->Close();
}

TEST_F(WidgetObserverTest, VisibilityChange) {
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  WidgetAutoclosePtr child1(NewWidget());
  WidgetAutoclosePtr child2(NewWidget());

  toplevel->Show();
  child1->Show();
  child2->Show();

  reset();

  child1->Hide();
  EXPECT_EQ(child1.get(), widget_hidden());

  child2->Hide();
  EXPECT_EQ(child2.get(), widget_hidden());

  child1->Show();
  EXPECT_EQ(child1.get(), widget_shown());

  child2->Show();
  EXPECT_EQ(child2.get(), widget_shown());
}

TEST_F(WidgetObserverTest, DestroyBubble) {
  // This test expect NativeWidgetAura, force its creation.
  ViewsDelegate::GetInstance()->set_native_widget_factory(
      ViewsDelegate::NativeWidgetFactory());

  WidgetAutoclosePtr anchor(CreateTopLevelPlatformWidget());
  anchor->Show();

  BubbleDialogDelegateView* bubble_delegate =
      new TestBubbleDialogDelegateView(anchor->client_view());
  {
    WidgetAutoclosePtr bubble_widget(
        BubbleDialogDelegateView::CreateBubble(bubble_delegate));
    bubble_widget->Show();
  }

  anchor->Hide();
}

TEST_F(WidgetObserverTest, WidgetBoundsChanged) {
  WidgetAutoclosePtr child1(NewWidget());
  WidgetAutoclosePtr child2(NewWidget());

  child1->OnNativeWidgetMove();
  EXPECT_EQ(child1.get(), widget_bounds_changed());

  child2->OnNativeWidgetMove();
  EXPECT_EQ(child2.get(), widget_bounds_changed());

  child1->OnNativeWidgetSizeChanged(gfx::Size());
  EXPECT_EQ(child1.get(), widget_bounds_changed());

  child2->OnNativeWidgetSizeChanged(gfx::Size());
  EXPECT_EQ(child2.get(), widget_bounds_changed());
}

// An extension to WidgetBoundsChanged to ensure notifications are forwarded
// by the NativeWidget implementation.
TEST_F(WidgetObserverTest, WidgetBoundsChangedNative) {
  // Don't use NewWidget(), so that the Init() flow can be observed to ensure
  // consistency across platforms.
  Widget* widget = new Widget();  // Note: owned by NativeWidget.
  widget->AddObserver(this);

  EXPECT_FALSE(widget_bounds_changed());

  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);

  // Use an origin within the work area since platforms (e.g. Mac) may move a
  // window into the work area when showing, triggering a bounds change.
  params.bounds = gfx::Rect(50, 50, 100, 100);

  // Init causes a bounds change, even while not showing. Note some platforms
  // cause a bounds change even when the bounds are empty. Mac does not.
  widget->Init(std::move(params));
  EXPECT_TRUE(widget_bounds_changed());
  reset();

  // Resizing while hidden, triggers a change.
  widget->SetSize(gfx::Size(160, 100));
  EXPECT_FALSE(widget->IsVisible());
  EXPECT_TRUE(widget_bounds_changed());
  reset();

  // Setting the same size does nothing.
  widget->SetSize(gfx::Size(160, 100));
  EXPECT_FALSE(widget_bounds_changed());
  reset();

  // Showing does nothing to the bounds.
  widget->Show();
  EXPECT_TRUE(widget->IsVisible());
  EXPECT_FALSE(widget_bounds_changed());
  reset();

  // Resizing while shown.
  widget->SetSize(gfx::Size(170, 100));
  EXPECT_TRUE(widget_bounds_changed());
  reset();

  // Resize to the same thing while shown does nothing.
  widget->SetSize(gfx::Size(170, 100));
  EXPECT_FALSE(widget_bounds_changed());
  reset();

  // Move, but don't change the size.
  widget->SetBounds(gfx::Rect(110, 110, 170, 100));
  EXPECT_TRUE(widget_bounds_changed());
  reset();

  // Moving to the same place does nothing.
  widget->SetBounds(gfx::Rect(110, 110, 170, 100));
  EXPECT_FALSE(widget_bounds_changed());
  reset();

  // No bounds change when closing.
  widget->CloseNow();
  EXPECT_FALSE(widget_bounds_changed());
}

namespace {

class MoveTrackingTestDesktopWidgetDelegate : public TestDesktopWidgetDelegate {
 public:
  int move_count() const { return move_count_; }

  // WidgetDelegate:
  void OnWidgetMove() override { ++move_count_; }

 private:
  int move_count_ = 0;
};

}  // namespace

class DesktopWidgetObserverTest : public WidgetObserverTest {
 public:
  DesktopWidgetObserverTest() = default;

  DesktopWidgetObserverTest(const DesktopWidgetObserverTest&) = delete;
  DesktopWidgetObserverTest& operator=(const DesktopWidgetObserverTest&) =
      delete;

  ~DesktopWidgetObserverTest() override = default;

  // WidgetObserverTest:
  void SetUp() override {
    set_native_widget_type(NativeWidgetType::kDesktop);
    WidgetObserverTest::SetUp();
  }
};

// An extension to the WidgetBoundsChangedNative test above to ensure move
// notifications propagate to the WidgetDelegate.
TEST_F(DesktopWidgetObserverTest, OnWidgetMovedWhenOriginChangesNative) {
  MoveTrackingTestDesktopWidgetDelegate delegate;
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  delegate.InitWidget(std::move(params));
  Widget* widget = delegate.GetWidget();
  widget->Show();
  widget->SetBounds(gfx::Rect(100, 100, 300, 200));

  const int moves_during_init = delegate.move_count();

  // Resize without changing origin. No move.
  widget->SetBounds(gfx::Rect(100, 100, 310, 210));
  EXPECT_EQ(moves_during_init, delegate.move_count());

  // Move without changing size. Moves.
  widget->SetBounds(gfx::Rect(110, 110, 310, 210));
  EXPECT_EQ(moves_during_init + 1, delegate.move_count());

  // Changing both moves.
  widget->SetBounds(gfx::Rect(90, 90, 330, 230));
  EXPECT_EQ(moves_during_init + 2, delegate.move_count());

  // Just grow vertically. On Mac, this changes the AppKit origin since it is
  // from the bottom left of the screen, but there is no move as far as views is
  // concerned.
  widget->SetBounds(gfx::Rect(90, 90, 330, 240));
  // No change.
  EXPECT_EQ(moves_during_init + 2, delegate.move_count());

  // For a similar reason, move the widget down by the same amount that it grows
  // vertically. The AppKit origin does not change, but it is a move.
  widget->SetBounds(gfx::Rect(90, 100, 330, 250));
  EXPECT_EQ(moves_during_init + 3, delegate.move_count());
}

// Test correct behavior when widgets close themselves in response to visibility
// changes.
TEST_F(WidgetObserverTest, ClosingOnHiddenParent) {
  WidgetAutoclosePtr parent(NewWidget());
  Widget* child = CreateChildPlatformWidget(parent->GetNativeView());

  TestWidgetObserver child_observer(child);

  EXPECT_FALSE(parent->IsVisible());
  EXPECT_FALSE(child->IsVisible());

  // Note |child| is TYPE_CONTROL, which start shown. So no need to show the
  // child separately.
  parent->Show();
  EXPECT_TRUE(parent->IsVisible());
  EXPECT_TRUE(child->IsVisible());

  // Simulate a child widget that closes itself when the parent is hidden.
  CloseOnNextHide(child);
  EXPECT_FALSE(child_observer.widget_closed());
  parent->Hide();
  RunPendingMessages();
  EXPECT_TRUE(child_observer.widget_closed());
}

// Test behavior of NativeWidget*::GetWindowPlacement on the native desktop.
#if BUILDFLAG(IS_LINUX)
// On desktop-Linux cheat and use non-desktop widgets. On X11, minimize is
// asynchronous. Also (harder) showing a window doesn't activate it without
// user interaction (or extra steps only done for interactive ui tests).
// Without that, show_state remains in ui::SHOW_STATE_INACTIVE throughout.
// TODO(tapted): Find a nice way to run this with desktop widgets on Linux.
TEST_F(WidgetTest, GetWindowPlacement) {
#else
TEST_F(DesktopWidgetTest, GetWindowPlacement) {
#endif
  WidgetAutoclosePtr widget;
  widget.reset(CreateTopLevelNativeWidget());

  gfx::Rect expected_bounds(100, 110, 200, 220);
  widget->SetBounds(expected_bounds);
  widget->Show();

  // Start with something invalid to ensure it changes.
  ui::WindowShowState show_state = ui::SHOW_STATE_END;
  gfx::Rect restored_bounds;

  internal::NativeWidgetPrivate* native_widget =
      widget->native_widget_private();

  native_widget->GetWindowPlacement(&restored_bounds, &show_state);
  EXPECT_EQ(expected_bounds, restored_bounds);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  // Non-desktop/Ash widgets start off in "default" until a Restore().
  EXPECT_EQ(ui::SHOW_STATE_DEFAULT, show_state);
  widget->Restore();
  native_widget->GetWindowPlacement(&restored_bounds, &show_state);
#endif
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, show_state);

  views::test::PropertyWaiter minimize_waiter(
      base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
      true);
  widget->Minimize();
  EXPECT_TRUE(minimize_waiter.Wait());

  native_widget->GetWindowPlacement(&restored_bounds, &show_state);
  EXPECT_EQ(ui::SHOW_STATE_MINIMIZED, show_state);
  EXPECT_EQ(expected_bounds, restored_bounds);

  views::test::PropertyWaiter restore_waiter(
      base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
      false);
  widget->Restore();
  EXPECT_TRUE(restore_waiter.Wait());

  native_widget->GetWindowPlacement(&restored_bounds, &show_state);
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, show_state);
  EXPECT_EQ(expected_bounds, restored_bounds);

  expected_bounds = gfx::Rect(130, 140, 230, 250);
  widget->SetBounds(expected_bounds);
  native_widget->GetWindowPlacement(&restored_bounds, &show_state);
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, show_state);
  EXPECT_EQ(expected_bounds, restored_bounds);

  widget->SetFullscreen(true);
  native_widget->GetWindowPlacement(&restored_bounds, &show_state);

#if BUILDFLAG(IS_WIN)
  // Desktop Aura widgets on Windows currently don't update show_state when
  // going fullscreen, and report restored_bounds as the full screen size.
  // See http://crbug.com/475813.
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, show_state);
#else
  EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, show_state);
  EXPECT_EQ(expected_bounds, restored_bounds);
#endif

  widget->SetFullscreen(false);
  native_widget->GetWindowPlacement(&restored_bounds, &show_state);
  EXPECT_EQ(ui::SHOW_STATE_NORMAL, show_state);
  EXPECT_EQ(expected_bounds, restored_bounds);
}

// Test that widget size constraints are properly applied immediately after
// Init(), and that SetBounds() calls are appropriately clamped.
TEST_F(DesktopWidgetTest, MinimumSizeConstraints) {
  TestDesktopWidgetDelegate delegate;
  gfx::Size minimum_size(100, 100);
  const gfx::Size smaller_size(90, 90);

  delegate.set_contents_view(new StaticSizedView(minimum_size));
  delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW));
  Widget* widget = delegate.GetWidget();

  // On desktop Linux, the Widget must be shown to ensure the window is mapped.
  // On other platforms this line is optional.
  widget->Show();

  // Sanity checks.
  EXPECT_GT(delegate.initial_bounds().width(), minimum_size.width());
  EXPECT_GT(delegate.initial_bounds().height(), minimum_size.height());
  EXPECT_EQ(delegate.initial_bounds().size(),
            widget->GetWindowBoundsInScreen().size());
  // Note: StaticSizedView doesn't currently provide a maximum size.
  EXPECT_EQ(gfx::Size(), widget->GetMaximumSize());

  if (!widget->ShouldUseNativeFrame()) {
    // The test environment may have dwm disabled on Windows. In this case,
    // CustomFrameView is used instead of the NativeFrameView, which will
    // provide a minimum size that includes frame decorations.
    minimum_size = widget->non_client_view()
                       ->GetWindowBoundsForClientBounds(gfx::Rect(minimum_size))
                       .size();
  }

  EXPECT_EQ(minimum_size, widget->GetMinimumSize());
  EXPECT_EQ(minimum_size, GetNativeWidgetMinimumContentSize(widget));

  // Trying to resize smaller than the minimum size should restrict the content
  // size to the minimum size.
  widget->SetBounds(gfx::Rect(smaller_size));
  EXPECT_EQ(minimum_size, widget->GetClientAreaBoundsInScreen().size());

  widget->SetSize(smaller_size);
  EXPECT_EQ(minimum_size, widget->GetClientAreaBoundsInScreen().size());
}

// When a non-desktop widget has a desktop child widget, due to the
// async nature of desktop widget shutdown, the parent can be destroyed before
// its child. Make sure that parent() returns nullptr at this time.
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
TEST_F(DesktopWidgetTest, GetPossiblyDestroyedParent) {
  WidgetAutoclosePtr root(CreateTopLevelNativeWidget());

  const auto create_widget = [](Widget* parent, bool is_desktop) {
    Widget* widget = new Widget;
    Widget::InitParams init_params(Widget::InitParams::TYPE_WINDOW);
    init_params.parent = parent->GetNativeView();
    init_params.context = parent->GetNativeView();
    if (is_desktop) {
      init_params.native_widget =
          new test::TestPlatformNativeWidget<DesktopNativeWidgetAura>(
              widget, false, nullptr);
    } else {
      init_params.native_widget =
          new test::TestPlatformNativeWidget<NativeWidgetAura>(widget, false,
                                                               nullptr);
    }
    widget->Init(std::move(init_params));
    return widget;
  };

  WidgetAutoclosePtr child(create_widget(root.get(), /* non-desktop */ false));
  WidgetAutoclosePtr grandchild(create_widget(child.get(), /* desktop */ true));

  child.reset();
  EXPECT_EQ(grandchild->parent(), nullptr);
}
#endif  // BUILDFLAG(ENABLE_DESKTOP_AURA)

// Tests that SetBounds() and GetWindowBoundsInScreen() is symmetric when the
// widget is visible and not maximized or fullscreen.
TEST_F(WidgetTest, GetWindowBoundsInScreen) {
  // Choose test coordinates away from edges and dimensions that are "small"
  // (but not too small) to ensure the OS doesn't try to adjust them.
  const gfx::Rect kTestBounds(150, 150, 400, 300);
  const gfx::Size kTestSize(200, 180);

  {
    // First test a toplevel widget.
    WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
    widget->Show();

    EXPECT_NE(kTestSize.ToString(),
              widget->GetWindowBoundsInScreen().size().ToString());
    widget->SetSize(kTestSize);
    EXPECT_EQ(kTestSize.ToString(),
              widget->GetWindowBoundsInScreen().size().ToString());

    EXPECT_NE(kTestBounds.ToString(),
              widget->GetWindowBoundsInScreen().ToString());
    widget->SetBounds(kTestBounds);
    EXPECT_EQ(kTestBounds.ToString(),
              widget->GetWindowBoundsInScreen().ToString());

    // Changing just the size should not change the origin.
    widget->SetSize(kTestSize);
    EXPECT_EQ(kTestBounds.origin().ToString(),
              widget->GetWindowBoundsInScreen().origin().ToString());
  }

  // Same tests with a frameless window.
  WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget());
  widget->Show();

  EXPECT_NE(kTestSize.ToString(),
            widget->GetWindowBoundsInScreen().size().ToString());
  widget->SetSize(kTestSize);
  EXPECT_EQ(kTestSize.ToString(),
            widget->GetWindowBoundsInScreen().size().ToString());

  EXPECT_NE(kTestBounds.ToString(),
            widget->GetWindowBoundsInScreen().ToString());
  widget->SetBounds(kTestBounds);
  EXPECT_EQ(kTestBounds.ToString(),
            widget->GetWindowBoundsInScreen().ToString());

  // For a frameless widget, the client bounds should also match.
  EXPECT_EQ(kTestBounds.ToString(),
            widget->GetClientAreaBoundsInScreen().ToString());

  // Verify origin is stable for a frameless window as well.
  widget->SetSize(kTestSize);
  EXPECT_EQ(kTestBounds.origin().ToString(),
            widget->GetWindowBoundsInScreen().origin().ToString());
}

// Chrome OS widgets need the shell to maximize/fullscreen window.
// Disable on desktop Linux because windows restore to the wrong bounds.
// See http://crbug.com/515369.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
#define MAYBE_GetRestoredBounds DISABLED_GetRestoredBounds
#else
#define MAYBE_GetRestoredBounds GetRestoredBounds
#endif

// Test that GetRestoredBounds() returns the original bounds of the window.
TEST_F(DesktopWidgetTest, MAYBE_GetRestoredBounds) {
  WidgetAutoclosePtr toplevel(CreateTopLevelNativeWidget());
  toplevel->Show();
  // Initial restored bounds have non-zero size.
  EXPECT_FALSE(toplevel->GetRestoredBounds().IsEmpty());

  const gfx::Rect bounds(100, 100, 200, 200);
  toplevel->SetBounds(bounds);
  EXPECT_EQ(bounds, toplevel->GetWindowBoundsInScreen());
  EXPECT_EQ(bounds, toplevel->GetRestoredBounds());

  toplevel->Maximize();
  RunPendingMessages();
#if BUILDFLAG(IS_MAC)
  // Current expectation on Mac is to do nothing on Maximize.
  EXPECT_EQ(toplevel->GetWindowBoundsInScreen(), toplevel->GetRestoredBounds());
#else
  EXPECT_NE(toplevel->GetWindowBoundsInScreen(), toplevel->GetRestoredBounds());
#endif
  EXPECT_EQ(bounds, toplevel->GetRestoredBounds());

  toplevel->Restore();
  RunPendingMessages();
  EXPECT_EQ(bounds, toplevel->GetWindowBoundsInScreen());
  EXPECT_EQ(bounds, toplevel->GetRestoredBounds());

  toplevel->SetFullscreen(true);
  RunPendingMessages();

  EXPECT_NE(toplevel->GetWindowBoundsInScreen(), toplevel->GetRestoredBounds());
  EXPECT_EQ(bounds, toplevel->GetRestoredBounds());

  toplevel->SetFullscreen(false);
  RunPendingMessages();
  EXPECT_EQ(bounds, toplevel->GetWindowBoundsInScreen());
  EXPECT_EQ(bounds, toplevel->GetRestoredBounds());
}

// The key-event propagation from Widget happens differently on aura and
// non-aura systems because of the difference in IME. So this test works only on
// aura.
TEST_F(WidgetTest, KeyboardInputEvent) {
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  View* container = toplevel->client_view();

  Textfield* textfield = new Textfield();
  textfield->SetText(u"some text");
  container->AddChildView(textfield);
  toplevel->Show();
  textfield->RequestFocus();

  // The press gets handled. The release doesn't have an effect.
  ui::KeyEvent backspace_p(ui::ET_KEY_PRESSED, ui::VKEY_DELETE, ui::EF_NONE);
  toplevel->OnKeyEvent(&backspace_p);
  EXPECT_TRUE(backspace_p.stopped_propagation());
  ui::KeyEvent backspace_r(ui::ET_KEY_RELEASED, ui::VKEY_DELETE, ui::EF_NONE);
  toplevel->OnKeyEvent(&backspace_r);
  EXPECT_FALSE(backspace_r.handled());
}

TEST_F(WidgetTest, BubbleControlsResetOnInit) {
  WidgetAutoclosePtr anchor(CreateTopLevelPlatformWidget());
  anchor->Show();

  {
    TestBubbleDialogDelegateView* bubble_delegate =
        new TestBubbleDialogDelegateView(anchor->client_view());
    WidgetAutoclosePtr bubble_widget(
        BubbleDialogDelegateView::CreateBubble(bubble_delegate));
    EXPECT_TRUE(bubble_delegate->reset_controls_called_);
    bubble_widget->Show();
  }

  anchor->Hide();
}

#if BUILDFLAG(IS_WIN)
// Test to ensure that after minimize, view width is set to zero. This is only
// the case for desktop widgets on Windows. Other platforms retain the window
// size while minimized.
TEST_F(DesktopWidgetTest, TestViewWidthAfterMinimizingWidget) {
  // Create a widget.
  std::unique_ptr<Widget> widget =
      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
  NonClientView* non_client_view = widget->non_client_view();
  non_client_view->SetFrameView(
      std::make_unique<MinimumSizeFrameView>(widget.get()));
  // Setting the frame view doesn't do a layout, so force one.
  non_client_view->InvalidateLayout();
  views::test::RunScheduledLayout(non_client_view);
  widget->Show();
  EXPECT_NE(0, non_client_view->frame_view()->width());
  widget->Minimize();
  EXPECT_EQ(0, non_client_view->frame_view()->width());
}
#endif

// Desktop native widget Aura tests are for non Chrome OS platforms.
// This class validates whether paints are received for a visible Widget.
// It observes Widget visibility and Close() and tracks whether subsequent
// paints are expected.
class DesktopAuraTestValidPaintWidget : public Widget, public WidgetObserver {
 public:
  explicit DesktopAuraTestValidPaintWidget(Widget::InitParams init_params)
      : Widget(std::move(init_params)) {
    observation_.Observe(this);
  }

  DesktopAuraTestValidPaintWidget(const DesktopAuraTestValidPaintWidget&) =
      delete;
  DesktopAuraTestValidPaintWidget& operator=(
      const DesktopAuraTestValidPaintWidget&) = delete;

  ~DesktopAuraTestValidPaintWidget() override = default;

  bool ReadReceivedPaintAndReset() {
    return std::exchange(received_paint_, false);
  }

  bool received_paint_while_hidden() const {
    return received_paint_while_hidden_;
  }

  void WaitUntilPaint() {
    if (received_paint_)
      return;
    base::RunLoop runloop;
    quit_closure_ = runloop.QuitClosure();
    runloop.Run();
    quit_closure_.Reset();
  }

  // WidgetObserver:
  void OnWidgetDestroying(Widget* widget) override { expect_paint_ = false; }

  void OnNativeWidgetPaint(const ui::PaintContext& context) override {
    received_paint_ = true;
    EXPECT_TRUE(expect_paint_);
    if (!expect_paint_)
      received_paint_while_hidden_ = true;
    views::Widget::OnNativeWidgetPaint(context);
    if (!quit_closure_.is_null())
      std::move(quit_closure_).Run();
  }

  void OnWidgetVisibilityChanged(Widget* widget, bool visible) override {
    expect_paint_ = visible;
  }

 private:
  bool received_paint_ = false;
  bool expect_paint_ = true;
  bool received_paint_while_hidden_ = false;
  base::OnceClosure quit_closure_;
  base::ScopedObservation<Widget, WidgetObserver> observation_{this};
};

class DesktopAuraPaintWidgetTest : public DesktopWidgetTest {
 public:
  std::unique_ptr<views::Widget> CreateTestWidget(
      views::Widget::InitParams::Type type =
          views::Widget::InitParams::TYPE_WINDOW_FRAMELESS) override {
    auto widget = std::make_unique<DesktopAuraTestValidPaintWidget>(
        CreateParamsForTestWidget(type));
    paint_widget_ = widget.get();

    View* contents_view =
        widget->SetContentsView(std::make_unique<ContentsView>());
    contents_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);

    widget->Show();
    widget->Activate();

    return widget;
  }

  DesktopAuraTestValidPaintWidget* paint_widget() { return paint_widget_; }

 private:
  class ContentsView : public View {
    void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
      node_data->SetNameExplicitlyEmpty();
      // Focusable Views need a valid role.
      node_data->role = ax::mojom::Role::kDialog;
    }
  };

  raw_ptr<DesktopAuraTestValidPaintWidget> paint_widget_ = nullptr;
};

TEST_F(DesktopAuraPaintWidgetTest, DesktopNativeWidgetNoPaintAfterCloseTest) {
  std::unique_ptr<Widget> widget = CreateTestWidget();
  paint_widget()->WaitUntilPaint();
  EXPECT_TRUE(paint_widget()->ReadReceivedPaintAndReset());
  widget->SchedulePaintInRect(widget->GetRestoredBounds());
  widget->Close();
  RunPendingMessages();
  EXPECT_FALSE(paint_widget()->ReadReceivedPaintAndReset());
  EXPECT_FALSE(paint_widget()->received_paint_while_hidden());
}

TEST_F(DesktopAuraPaintWidgetTest, DesktopNativeWidgetNoPaintAfterHideTest) {
  std::unique_ptr<Widget> widget = CreateTestWidget();
  paint_widget()->WaitUntilPaint();
  EXPECT_TRUE(paint_widget()->ReadReceivedPaintAndReset());
  widget->SchedulePaintInRect(widget->GetRestoredBounds());
  widget->Hide();
  RunPendingMessages();
  EXPECT_FALSE(paint_widget()->ReadReceivedPaintAndReset());
  EXPECT_FALSE(paint_widget()->received_paint_while_hidden());
  widget->Close();
}

// Test to ensure that the aura Window's visibility state is set to visible if
// the underlying widget is hidden and then shown.
TEST_F(DesktopWidgetTest, TestWindowVisibilityAfterHide) {
  // Create a widget.
  std::unique_ptr<Widget> widget =
      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
  NonClientView* non_client_view = widget->non_client_view();
  non_client_view->SetFrameView(
      std::make_unique<MinimumSizeFrameView>(widget.get()));

  widget->Show();
  EXPECT_TRUE(IsNativeWindowVisible(widget->GetNativeWindow()));
  widget->Hide();
  EXPECT_FALSE(IsNativeWindowVisible(widget->GetNativeWindow()));
  widget->Show();
  EXPECT_TRUE(IsNativeWindowVisible(widget->GetNativeWindow()));
}

// Tests that wheel events generated from scroll events are targeted to the
// views under the cursor when the focused view does not processed them.
TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) {
  EventCountView* cursor_view = new EventCountView;
  cursor_view->SetBounds(60, 0, 50, 40);

  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  widget->GetRootView()->AddChildView(cursor_view);

  // Generate a scroll event on the cursor view.
  ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(65, 5),
                         ui::EventTimeForNow(), 0, 0, 20, 0, 20, 2);
  widget->OnScrollEvent(&scroll);

  EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_SCROLL));
  EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL));

  cursor_view->ResetCounts();

  ui::ScrollEvent scroll2(ui::ET_SCROLL, gfx::Point(5, 5),
                          ui::EventTimeForNow(), 0, 0, 20, 0, 20, 2);
  widget->OnScrollEvent(&scroll2);

  EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_SCROLL));
  EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL));
}

// Tests that if a scroll-begin gesture is not handled, then subsequent scroll
// events are not dispatched to any view.
TEST_F(WidgetTest, GestureScrollEventDispatching) {
  EventCountView* noscroll_view = new EventCountView;
  EventCountView* scroll_view = new ScrollableEventCountView;

  noscroll_view->SetBounds(0, 0, 50, 40);
  scroll_view->SetBounds(60, 0, 40, 40);

  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  widget->GetRootView()->AddChildView(noscroll_view);
  widget->GetRootView()->AddChildView(scroll_view);

  {
    ui::GestureEvent begin =
        CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN, 5, 5);
    widget->OnGestureEvent(&begin);
    ui::GestureEvent update = CreateTestGestureEvent(
        ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), 25, 15);
    widget->OnGestureEvent(&update);
    ui::GestureEvent end =
        CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_END, 25, 15);
    widget->OnGestureEvent(&end);

    EXPECT_EQ(1, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
    EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
    EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  }

  {
    ui::GestureEvent begin =
        CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN, 65, 5);
    widget->OnGestureEvent(&begin);
    ui::GestureEvent update = CreateTestGestureEvent(
        ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10), 85, 15);
    widget->OnGestureEvent(&update);
    ui::GestureEvent end =
        CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_END, 85, 15);
    widget->OnGestureEvent(&end);

    EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
    EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
    EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  }
}

// Tests that event-handlers installed on the RootView get triggered correctly.
// TODO(tdanderson): Clean up this test as part of crbug.com/355680.
TEST_F(WidgetTest, EventHandlersOnRootView) {
  WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
  View* root_view = widget->GetRootView();

  EventCountView* view =
      root_view->AddChildView(std::make_unique<EventCountView>());
  view->SetBounds(0, 0, 20, 20);

  EventCountHandler h1;
  root_view->AddPreTargetHandler(&h1);

  EventCountHandler h2;
  root_view->AddPostTargetHandler(&h2);

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

  // Dispatch a ui::ET_SCROLL event. The event remains unhandled and should
  // bubble up the views hierarchy to be re-dispatched on the root view.
  ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(5, 5), ui::EventTimeForNow(),
                         0, 0, 20, 0, 20, 2);
  widget->OnScrollEvent(&scroll);
  EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL));
  EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL));

  // Unhandled scroll events are turned into wheel events and re-dispatched.
  EXPECT_EQ(1, h1.GetEventCount(ui::ET_MOUSEWHEEL));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSEWHEEL));
  EXPECT_EQ(1, h2.GetEventCount(ui::ET_MOUSEWHEEL));

  h1.ResetCounts();
  view->ResetCounts();
  h2.ResetCounts();

  // Dispatch a ui::ET_SCROLL_FLING_START event. The event remains unhandled and
  // should bubble up the views hierarchy to be re-dispatched on the root view.
  ui::ScrollEvent fling(ui::ET_SCROLL_FLING_START, gfx::Point(5, 5),
                        ui::EventTimeForNow(), 0, 0, 20, 0, 20, 2);
  widget->OnScrollEvent(&fling);
  EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL_FLING_START));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL_FLING_START));
  EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL_FLING_START));

  // Unhandled scroll events which are not of type ui::ET_SCROLL should not
  // be turned into wheel events and re-dispatched.
  EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL));
  EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSEWHEEL));
  EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL));

  h1.ResetCounts();
  view->ResetCounts();
  h2.ResetCounts();

  // Change the handle mode of |view| so that events are marked as handled at
  // the target phase.
  view->set_handle_mode(EventCountView::CONSUME_EVENTS);

  // Dispatch a ui::ET_GESTURE_TAP_DOWN and a ui::ET_GESTURE_TAP_CANCEL event.
  // The events are handled at the target phase and should not reach the
  // post-target handler.
  ui::GestureEvent tap_down =
      CreateTestGestureEvent(ui::ET_GESTURE_TAP_DOWN, 5, 5);
  widget->OnGestureEvent(&tap_down);
  EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_TAP_DOWN));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_TAP_DOWN));
  EXPECT_EQ(0, h2.GetEventCount(ui::ET_GESTURE_TAP_DOWN));

  ui::GestureEvent tap_cancel =
      CreateTestGestureEvent(ui::ET_GESTURE_TAP_CANCEL, 5, 5);
  widget->OnGestureEvent(&tap_cancel);
  EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_TAP_CANCEL));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_TAP_CANCEL));
  EXPECT_EQ(0, h2.GetEventCount(ui::ET_GESTURE_TAP_CANCEL));

  h1.ResetCounts();
  view->ResetCounts();
  h2.ResetCounts();

  // Dispatch a ui::ET_SCROLL event. The event is handled at the target phase
  // and should not reach the post-target handler.
  ui::ScrollEvent consumed_scroll(ui::ET_SCROLL, gfx::Point(5, 5),
                                  ui::EventTimeForNow(), 0, 0, 20, 0, 20, 2);
  widget->OnScrollEvent(&consumed_scroll);
  EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL));
  EXPECT_EQ(0, h2.GetEventCount(ui::ET_SCROLL));

  // Handled scroll events are not turned into wheel events and re-dispatched.
  EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL));
  EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSEWHEEL));
  EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL));

  root_view->RemovePreTargetHandler(&h1);
}

TEST_F(WidgetTest, SynthesizeMouseMoveEvent) {
  WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
  View* root_view = widget->GetRootView();
  widget->SetBounds(gfx::Rect(0, 0, 100, 100));

  EventCountView* v1 =
      root_view->AddChildView(std::make_unique<EventCountView>());
  v1->SetBounds(5, 5, 10, 10);
  EventCountView* v2 =
      root_view->AddChildView(std::make_unique<EventCountView>());
  v2->SetBounds(5, 15, 10, 10);

  widget->Show();

  // SynthesizeMouseMoveEvent does nothing until the mouse is entered.
  widget->SynthesizeMouseMoveEvent();
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED));

  gfx::Point cursor_location(v1->GetBoundsInScreen().CenterPoint());
  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());
  generator->MoveMouseTo(cursor_location);

  EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED));

  // SynthesizeMouseMoveEvent dispatches an mousemove event.
  widget->SynthesizeMouseMoveEvent();
  EXPECT_EQ(2, v1->GetEventCount(ui::ET_MOUSE_MOVED));

  root_view->RemoveChildViewT(v1);
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED));
  v2->SetBounds(5, 5, 10, 10);
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_MOVED));

  widget->SynthesizeMouseMoveEvent();
  EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_MOVED));
}

namespace {

// ui::EventHandler which handles all mouse press events.
class MousePressEventConsumer : public ui::EventHandler {
 public:
  MousePressEventConsumer() = default;

  MousePressEventConsumer(const MousePressEventConsumer&) = delete;
  MousePressEventConsumer& operator=(const MousePressEventConsumer&) = delete;

 private:
  // ui::EventHandler:
  void OnMouseEvent(ui::MouseEvent* event) override {
    if (event->type() == ui::ET_MOUSE_PRESSED)
      event->SetHandled();
  }
};

}  // namespace

// No touch on desktop Mac. Tracked in http://crbug.com/445520.
#if !BUILDFLAG(IS_MAC) || defined(USE_AURA)

// Test that mouse presses and mouse releases are dispatched normally when a
// touch is down.
TEST_F(WidgetTest, MouseEventDispatchWhileTouchIsDown) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->Show();
  widget->SetSize(gfx::Size(300, 300));

  EventCountView* event_count_view =
      widget->GetRootView()->AddChildView(std::make_unique<EventCountView>());
  event_count_view->SetBounds(0, 0, 300, 300);

  MousePressEventConsumer consumer;
  event_count_view->AddPostTargetHandler(&consumer);

  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());
  generator->PressTouch();
  generator->ClickLeftButton();

  EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_RELEASED));

  // For mus it's important we destroy the widget before the EventGenerator.
  widget->CloseNow();
}

#endif  // !BUILDFLAG(IS_MAC) || defined(USE_AURA)

// Tests that when there is no active capture, that a mouse press causes capture
// to be set.
TEST_F(WidgetTest, MousePressCausesCapture) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->Show();
  widget->SetSize(gfx::Size(300, 300));

  EventCountView* event_count_view =
      widget->GetRootView()->AddChildView(std::make_unique<EventCountView>());
  event_count_view->SetBounds(0, 0, 300, 300);

  // No capture has been set.
  EXPECT_EQ(
      gfx::kNullNativeView,
      internal::NativeWidgetPrivate::GetGlobalCapture(widget->GetNativeView()));

  MousePressEventConsumer consumer;
  event_count_view->AddPostTargetHandler(&consumer);
  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());
  generator->MoveMouseTo(widget->GetClientAreaBoundsInScreen().CenterPoint());
  generator->PressLeftButton();

  EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_EQ(
      widget->GetNativeView(),
      internal::NativeWidgetPrivate::GetGlobalCapture(widget->GetNativeView()));

  // For mus it's important we destroy the widget before the EventGenerator.
  widget->CloseNow();
}

namespace {

// An EventHandler which shows a Wiget upon receiving a mouse event. The Widget
// proceeds to take capture.
class CaptureEventConsumer : public ui::EventHandler {
 public:
  explicit CaptureEventConsumer(Widget* widget)
      : event_count_view_(new EventCountView()), widget_(widget) {}

  CaptureEventConsumer(const CaptureEventConsumer&) = delete;
  CaptureEventConsumer& operator=(const CaptureEventConsumer&) = delete;

  ~CaptureEventConsumer() override { widget_->CloseNow(); }

 private:
  // ui::EventHandler:
  void OnMouseEvent(ui::MouseEvent* event) override {
    if (event->type() == ui::ET_MOUSE_PRESSED) {
      event->SetHandled();
      widget_->Show();
      widget_->SetSize(gfx::Size(200, 200));

      event_count_view_->SetBounds(0, 0, 200, 200);
      widget_->GetRootView()->AddChildView(event_count_view_.get());
      widget_->SetCapture(event_count_view_);
    }
  }

  raw_ptr<EventCountView> event_count_view_;
  raw_ptr<Widget> widget_;
};

}  // namespace

// Tests that if explicit capture occurs during a mouse press, that implicit
// capture is not applied.
TEST_F(WidgetTest, CaptureDuringMousePressNotOverridden) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->Show();
  widget->SetSize(gfx::Size(300, 300));

  EventCountView* event_count_view =
      widget->GetRootView()->AddChildView(std::make_unique<EventCountView>());
  event_count_view->SetBounds(0, 0, 300, 300);

  EXPECT_EQ(
      gfx::kNullNativeView,
      internal::NativeWidgetPrivate::GetGlobalCapture(widget->GetNativeView()));

  Widget* widget2 = CreateTopLevelNativeWidget();
  // Gives explicit capture to |widget2|
  CaptureEventConsumer consumer(widget2);
  event_count_view->AddPostTargetHandler(&consumer);
  auto generator =
      CreateEventGenerator(GetRootWindow(widget), widget->GetNativeWindow());
  generator->MoveMouseTo(widget->GetClientAreaBoundsInScreen().CenterPoint());
  // This event should implicitly give capture to |widget|, except that
  // |consumer| will explicitly set capture on |widget2|.
  generator->PressLeftButton();

  EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_NE(
      widget->GetNativeView(),
      internal::NativeWidgetPrivate::GetGlobalCapture(widget->GetNativeView()));
  EXPECT_EQ(
      widget2->GetNativeView(),
      internal::NativeWidgetPrivate::GetGlobalCapture(widget->GetNativeView()));

  // For mus it's important we destroy the widget before the EventGenerator.
  widget->CloseNow();
}

class ClosingEventObserver : public ui::EventObserver {
 public:
  explicit ClosingEventObserver(Widget* widget) : widget_(widget) {}

  ClosingEventObserver(const ClosingEventObserver&) = delete;
  ClosingEventObserver& operator=(const ClosingEventObserver&) = delete;

  // ui::EventObserver:
  void OnEvent(const ui::Event& event) override {
    // Guard against attempting to close the widget twice.
    if (widget_)
      widget_->CloseNow();
    widget_ = nullptr;
  }

 private:
  raw_ptr<Widget> widget_;
};

class ClosingView : public View {
 public:
  explicit ClosingView(Widget* widget) : widget_(widget) {}

  ClosingView(const ClosingView&) = delete;
  ClosingView& operator=(const ClosingView&) = delete;

  // View:
  void OnEvent(ui::Event* event) override {
    // Guard against closing twice and writing to freed memory.
    if (widget_ && event->type() == ui::ET_MOUSE_PRESSED) {
      Widget* widget = widget_;
      widget_ = nullptr;
      widget->CloseNow();
    }
  }

 private:
  raw_ptr<Widget> widget_;
};

// Ensures that when multiple objects are intercepting OS-level events, that one
// can safely close a Widget that has capture.
TEST_F(WidgetTest, DestroyedWithCaptureViaEventMonitor) {
  Widget* widget = CreateTopLevelNativeWidget();
  TestWidgetObserver observer(widget);
  widget->Show();
  widget->SetSize(gfx::Size(300, 300));

  // ClosingView and ClosingEventObserver both try to close the Widget. On Mac
  // the order that EventMonitors receive OS events is not deterministic. If the
  // one installed via SetCapture() sees it first, the event is swallowed (so
  // both need to try). Note the regression test would only fail when the
  // SetCapture() handler did _not_ swallow the event, but it still needs to try
  // to close the Widget otherwise it will be left open, which fails elsewhere.
  ClosingView* closing_view = widget->GetContentsView()->AddChildView(
      std::make_unique<ClosingView>(widget));
  widget->SetCapture(closing_view);

  ClosingEventObserver closing_event_observer(widget);
  auto monitor = EventMonitor::CreateApplicationMonitor(
      &closing_event_observer, widget->GetNativeWindow(),
      {ui::ET_MOUSE_PRESSED});

  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());
  generator->set_target(ui::test::EventGenerator::Target::APPLICATION);

  EXPECT_FALSE(observer.widget_closed());
  generator->PressLeftButton();
  EXPECT_TRUE(observer.widget_closed());
}

TEST_F(WidgetTest, LockPaintAsActive) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  widget->ShowInactive();
  EXPECT_FALSE(widget->ShouldPaintAsActive());

  // First lock causes widget to paint as active.
  auto lock = widget->LockPaintAsActive();
  EXPECT_TRUE(widget->ShouldPaintAsActive());

  // Second lock has no effect.
  auto lock2 = widget->LockPaintAsActive();
  EXPECT_TRUE(widget->ShouldPaintAsActive());

  // Have to release twice to get back to inactive state.
  lock2.reset();
  EXPECT_TRUE(widget->ShouldPaintAsActive());
  lock.reset();
  EXPECT_FALSE(widget->ShouldPaintAsActive());
}

TEST_F(WidgetTest, LockPaintAsActive_AlreadyActive) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  widget->Show();
  EXPECT_TRUE(widget->ShouldPaintAsActive());

  // Lock has no effect.
  auto lock = widget->LockPaintAsActive();
  EXPECT_TRUE(widget->ShouldPaintAsActive());

  // Remove lock has no effect.
  lock.reset();
  EXPECT_TRUE(widget->ShouldPaintAsActive());
}

TEST_F(WidgetTest, LockPaintAsActive_BecomesActive) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  widget->ShowInactive();
  EXPECT_FALSE(widget->ShouldPaintAsActive());

  // Lock toggles render mode.
  auto lock = widget->LockPaintAsActive();
  EXPECT_TRUE(widget->ShouldPaintAsActive());

  widget->Activate();

  // Remove lock has no effect.
  lock.reset();
  EXPECT_TRUE(widget->ShouldPaintAsActive());
}

class PaintAsActiveCallbackCounter {
 public:
  explicit PaintAsActiveCallbackCounter(Widget* widget) {
    // Subscribe to |widget|'s paint-as-active change.
    paint_as_active_subscription_ =
        widget->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
            &PaintAsActiveCallbackCounter::Call, base::Unretained(this)));
  }
  void Call() { count_++; }
  int CallCount() { return count_; }

 private:
  int count_ = 0;
  base::CallbackListSubscription paint_as_active_subscription_;
};

TEST_F(WidgetTest, LockParentPaintAsActive) {
  if (!PlatformStyle::kInactiveWidgetControlsAppearDisabled)
    return;

  WidgetAutoclosePtr parent(CreateTopLevelPlatformWidget());
  WidgetAutoclosePtr child(CreateChildPlatformWidget(parent->GetNativeView()));
  WidgetAutoclosePtr grandchild(
      CreateChildPlatformWidget(child->GetNativeView()));
  WidgetAutoclosePtr other(CreateTopLevelPlatformWidget());
  child->widget_delegate()->SetCanActivate(true);
  grandchild->widget_delegate()->SetCanActivate(true);

  PaintAsActiveCallbackCounter parent_control(parent.get());
  PaintAsActiveCallbackCounter child_control(child.get());
  PaintAsActiveCallbackCounter grandchild_control(grandchild.get());
  PaintAsActiveCallbackCounter other_control(other.get());

  parent->Show();
  EXPECT_TRUE(parent->ShouldPaintAsActive());
  EXPECT_TRUE(child->ShouldPaintAsActive());
  EXPECT_TRUE(grandchild->ShouldPaintAsActive());
  EXPECT_FALSE(other->ShouldPaintAsActive());
  EXPECT_EQ(parent_control.CallCount(), 1);
  EXPECT_EQ(child_control.CallCount(), 1);
  EXPECT_EQ(grandchild_control.CallCount(), 1);
  EXPECT_EQ(other_control.CallCount(), 0);

  other->Show();
  EXPECT_FALSE(parent->ShouldPaintAsActive());
  EXPECT_FALSE(child->ShouldPaintAsActive());
  EXPECT_FALSE(grandchild->ShouldPaintAsActive());
  EXPECT_TRUE(other->ShouldPaintAsActive());
  EXPECT_EQ(parent_control.CallCount(), 2);
  EXPECT_EQ(child_control.CallCount(), 2);
  EXPECT_EQ(grandchild_control.CallCount(), 2);
  EXPECT_EQ(other_control.CallCount(), 1);

  child->Show();
  EXPECT_TRUE(parent->ShouldPaintAsActive());
  EXPECT_TRUE(child->ShouldPaintAsActive());
  EXPECT_TRUE(grandchild->ShouldPaintAsActive());
  EXPECT_FALSE(other->ShouldPaintAsActive());
  EXPECT_EQ(parent_control.CallCount(), 3);
  EXPECT_EQ(child_control.CallCount(), 3);
  EXPECT_EQ(grandchild_control.CallCount(), 3);
  EXPECT_EQ(other_control.CallCount(), 2);

  other->Show();
  EXPECT_FALSE(parent->ShouldPaintAsActive());
  EXPECT_FALSE(child->ShouldPaintAsActive());
  EXPECT_FALSE(grandchild->ShouldPaintAsActive());
  EXPECT_TRUE(other->ShouldPaintAsActive());
  EXPECT_EQ(parent_control.CallCount(), 4);
  EXPECT_EQ(child_control.CallCount(), 4);
  EXPECT_EQ(grandchild_control.CallCount(), 4);
  EXPECT_EQ(other_control.CallCount(), 3);

  grandchild->Show();
  EXPECT_TRUE(parent->ShouldPaintAsActive());
  EXPECT_TRUE(child->ShouldPaintAsActive());
  EXPECT_TRUE(grandchild->ShouldPaintAsActive());
  EXPECT_FALSE(other->ShouldPaintAsActive());
  EXPECT_EQ(parent_control.CallCount(), 5);
  EXPECT_EQ(child_control.CallCount(), 5);
  EXPECT_EQ(grandchild_control.CallCount(), 5);
  EXPECT_EQ(other_control.CallCount(), 4);
}

// Tests to make sure that child widgets do not cause their parent widget to
// paint inactive immediately when they are closed. This avoids having the
// parent paint as inactive in the time between when the bubble is closed and
// when it's eventually destroyed by its native widget (see crbug.com/1303549).
TEST_F(DesktopWidgetTest,
       ClosingActiveChildDoesNotPrematurelyPaintParentInactive) {
  // top_level_widget that owns the bubble widget.
  auto top_level_widget = CreateTestWidget();
  top_level_widget->Show();

  // Create the child bubble widget.
  auto bubble_widget = std::make_unique<Widget>();
  Widget::InitParams init_params =
      CreateParamsForTestWidget(Widget::InitParams::TYPE_BUBBLE);
  init_params.parent = top_level_widget->GetNativeView();
  bubble_widget->Init(std::move(init_params));
  bubble_widget->Show();

  EXPECT_TRUE(bubble_widget->ShouldPaintAsActive());
  EXPECT_TRUE(top_level_widget->ShouldPaintAsActive());

  // Closing the bubble wiget should not immediately cause the top level widget
  // to paint inactive.
  PaintAsActiveCallbackCounter top_level_counter(top_level_widget.get());
  PaintAsActiveCallbackCounter bubble_counter(bubble_widget.get());
  bubble_widget->Close();
  EXPECT_FALSE(bubble_widget->ShouldPaintAsActive());
  EXPECT_TRUE(top_level_widget->ShouldPaintAsActive());
  EXPECT_EQ(top_level_counter.CallCount(), 0);
  EXPECT_EQ(bubble_counter.CallCount(), 0);
}

// Widget used to destroy itself when OnNativeWidgetDestroyed is called.
class TestNativeWidgetDestroyedWidget : public Widget {
 public:
  // Overridden from NativeWidgetDelegate:
  void OnNativeWidgetDestroyed() override;
};

void TestNativeWidgetDestroyedWidget::OnNativeWidgetDestroyed() {
  Widget::OnNativeWidgetDestroyed();
  delete this;
}

// Verifies that widget destroyed itself in OnNativeWidgetDestroyed does not
// crash in ASan.
TEST_F(DesktopWidgetTest, WidgetDestroyedItselfDoesNotCrash) {
  TestDesktopWidgetDelegate delegate(new TestNativeWidgetDestroyedWidget);
  delegate.InitWidget(CreateParamsForTestWidget());
  delegate.GetWidget()->Show();
  delegate.GetWidget()->CloseNow();
}

// Verifies WindowClosing() is invoked correctly on the delegate when a Widget
// is closed.
TEST_F(DesktopWidgetTest, SingleWindowClosing) {
  TestDesktopWidgetDelegate delegate;
  delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW));
  EXPECT_EQ(0, delegate.window_closing_count());
  delegate.GetWidget()->CloseNow();
  EXPECT_EQ(1, delegate.window_closing_count());
}

TEST_F(DesktopWidgetTest, CloseRequested_AllowsClose) {
  constexpr Widget::ClosedReason kReason = Widget::ClosedReason::kLostFocus;
  TestDesktopWidgetDelegate delegate;
  delegate.set_can_close(true);
  delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW));
  WidgetDestroyedWaiter waiter(delegate.GetWidget());

  delegate.GetWidget()->CloseWithReason(kReason);
  EXPECT_TRUE(delegate.GetWidget()->IsClosed());
  EXPECT_EQ(kReason, delegate.GetWidget()->closed_reason());
  EXPECT_EQ(kReason, delegate.last_closed_reason());

  waiter.Wait();
}

TEST_F(DesktopWidgetTest, CloseRequested_DisallowClose) {
  constexpr Widget::ClosedReason kReason = Widget::ClosedReason::kLostFocus;
  TestDesktopWidgetDelegate delegate;
  delegate.set_can_close(false);
  delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW));

  delegate.GetWidget()->CloseWithReason(kReason);
  EXPECT_FALSE(delegate.GetWidget()->IsClosed());
  EXPECT_EQ(Widget::ClosedReason::kUnspecified,
            delegate.GetWidget()->closed_reason());
  EXPECT_EQ(kReason, delegate.last_closed_reason());

  delegate.GetWidget()->CloseNow();
}

TEST_F(DesktopWidgetTest, CloseRequested_SecondCloseIgnored) {
  constexpr Widget::ClosedReason kReason1 = Widget::ClosedReason::kLostFocus;
  constexpr Widget::ClosedReason kReason2 = Widget::ClosedReason::kUnspecified;
  TestDesktopWidgetDelegate delegate;
  delegate.set_can_close(true);
  delegate.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW));
  WidgetDestroyedWaiter waiter(delegate.GetWidget());

  // Close for the first time.
  delegate.GetWidget()->CloseWithReason(kReason1);
  EXPECT_TRUE(delegate.GetWidget()->IsClosed());
  EXPECT_EQ(kReason1, delegate.last_closed_reason());

  // Calling close again should have no effect.
  delegate.GetWidget()->CloseWithReason(kReason2);
  EXPECT_TRUE(delegate.GetWidget()->IsClosed());
  EXPECT_EQ(kReason1, delegate.last_closed_reason());

  waiter.Wait();
}

class WidgetWindowTitleTest : public DesktopWidgetTest {
 protected:
  void RunTest(bool desktop_native_widget) {
    WidgetAutoclosePtr widget(new Widget());  // Destroyed by CloseNow().
    Widget::InitParams init_params =
        CreateParams(Widget::InitParams::TYPE_WINDOW);

    if (!desktop_native_widget) {
      init_params.native_widget =
          CreatePlatformNativeWidgetImpl(widget.get(), kStubCapture, nullptr);
    }
    widget->Init(std::move(init_params));

    internal::NativeWidgetPrivate* native_widget =
        widget->native_widget_private();

    std::u16string empty;
    std::u16string s1(u"Title1");
    std::u16string s2(u"Title2");
    std::u16string s3(u"TitleLong");

    // The widget starts with no title, setting empty should not change
    // anything.
    EXPECT_FALSE(native_widget->SetWindowTitle(empty));
    // Setting the title to something non-empty should cause a change.
    EXPECT_TRUE(native_widget->SetWindowTitle(s1));
    // Setting the title to something else with the same length should cause a
    // change.
    EXPECT_TRUE(native_widget->SetWindowTitle(s2));
    // Setting the title to something else with a different length should cause
    // a change.
    EXPECT_TRUE(native_widget->SetWindowTitle(s3));
    // Setting the title to the same thing twice should not cause a change.
    EXPECT_FALSE(native_widget->SetWindowTitle(s3));
  }
};

TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_NativeWidget) {
  // Use the default NativeWidget.
  bool desktop_native_widget = false;
  RunTest(desktop_native_widget);
}

TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) {
  // Override to use a DesktopNativeWidget.
  bool desktop_native_widget = true;
  RunTest(desktop_native_widget);
}

TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) {
  Widget* widget = new Widget;
  Widget::InitParams params =
      CreateParams(views::Widget::InitParams::TYPE_POPUP);
  widget->Init(std::move(params));

  widget->SetContentsView(
      std::make_unique<CloseWidgetView>(ui::ET_MOUSE_PRESSED));

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

  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());

  WidgetDeletionObserver deletion_observer(widget);
  generator->PressLeftButton();
  if (deletion_observer.IsWidgetAlive())
    generator->ReleaseLeftButton();
  EXPECT_FALSE(deletion_observer.IsWidgetAlive());

  // Yay we did not crash!
}

// No touch on desktop Mac. Tracked in http://crbug.com/445520.
#if !BUILDFLAG(IS_MAC) || defined(USE_AURA)

TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) {
  Widget* widget = new Widget;
  Widget::InitParams params =
      CreateParams(views::Widget::InitParams::TYPE_POPUP);
  widget->Init(std::move(params));

  widget->SetContentsView(
      std::make_unique<CloseWidgetView>(ui::ET_GESTURE_TAP_DOWN));

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

  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());

  WidgetDeletionObserver deletion_observer(widget);
  generator->GestureTapAt(widget->GetWindowBoundsInScreen().CenterPoint());
  EXPECT_FALSE(deletion_observer.IsWidgetAlive());

  // Yay we did not crash!
}

#endif  // !BUILDFLAG(IS_MAC) || defined(USE_AURA)

// See description of RunGetNativeThemeFromDestructor() for details.
class GetNativeThemeFromDestructorView : public WidgetDelegateView {
 public:
  GetNativeThemeFromDestructorView() = default;

  GetNativeThemeFromDestructorView(const GetNativeThemeFromDestructorView&) =
      delete;
  GetNativeThemeFromDestructorView& operator=(
      const GetNativeThemeFromDestructorView&) = delete;

  ~GetNativeThemeFromDestructorView() override { VerifyNativeTheme(); }

 private:
  void VerifyNativeTheme() { ASSERT_TRUE(GetNativeTheme() != nullptr); }
};

// Verifies GetNativeTheme() from the destructor of a WidgetDelegateView doesn't
// crash. |is_first_run| is true if this is the first call. A return value of
// true indicates this should be run again with a value of false.
// First run uses DesktopNativeWidgetAura (if possible). Second run doesn't.
bool RunGetNativeThemeFromDestructor(Widget::InitParams params,
                                     bool is_first_run) {
  bool needs_second_run = false;
  // Destroyed by CloseNow() below.
  WidgetTest::WidgetAutoclosePtr widget(new Widget);
  // Deletes itself when the Widget is destroyed.
  params.delegate = new GetNativeThemeFromDestructorView;
  if (!is_first_run) {
    params.native_widget =
        CreatePlatformNativeWidgetImpl(widget.get(), kStubCapture, nullptr);
    needs_second_run = true;
  }
  widget->Init(std::move(params));
  return needs_second_run;
}

// See description of RunGetNativeThemeFromDestructor() for details.
TEST_F(DesktopWidgetTest, GetNativeThemeFromDestructor) {
  if (RunGetNativeThemeFromDestructor(
          CreateParams(Widget::InitParams::TYPE_POPUP), true)) {
    RunGetNativeThemeFromDestructor(
        CreateParams(Widget::InitParams::TYPE_POPUP), false);
  }
}

// Used by HideCloseDestroy. Allows setting a boolean when the widget is
// destroyed.
class CloseDestroysWidget : public Widget {
 public:
  CloseDestroysWidget(bool* destroyed, base::OnceClosure quit_closure)
      : destroyed_(destroyed), quit_closure_(std::move(quit_closure)) {
    DCHECK(destroyed_);
    DCHECK(quit_closure_);
  }

  CloseDestroysWidget(const CloseDestroysWidget&) = delete;
  CloseDestroysWidget& operator=(const CloseDestroysWidget&) = delete;

  ~CloseDestroysWidget() override {
    *destroyed_ = true;
    std::move(quit_closure_).Run();
  }

  void Detach() { destroyed_ = nullptr; }

 private:
  raw_ptr<bool> destroyed_;
  base::OnceClosure quit_closure_;
};

// An observer that registers that an animation has ended.
class AnimationEndObserver : public ui::ImplicitAnimationObserver {
 public:
  AnimationEndObserver() = default;

  AnimationEndObserver(const AnimationEndObserver&) = delete;
  AnimationEndObserver& operator=(const AnimationEndObserver&) = delete;

  ~AnimationEndObserver() override = default;

  bool animation_completed() const { return animation_completed_; }

  // ui::ImplicitAnimationObserver:
  void OnImplicitAnimationsCompleted() override { animation_completed_ = true; }

 private:
  bool animation_completed_ = false;
};

// An observer that registers the bounds of a widget on destruction.
class WidgetBoundsObserver : public WidgetObserver {
 public:
  WidgetBoundsObserver() = default;

  WidgetBoundsObserver(const WidgetBoundsObserver&) = delete;
  WidgetBoundsObserver& operator=(const WidgetBoundsObserver&) = delete;

  ~WidgetBoundsObserver() override = default;

  gfx::Rect bounds() { return bounds_; }

  // WidgetObserver:
  void OnWidgetDestroying(Widget* widget) override {
    EXPECT_TRUE(widget->GetNativeWindow());
    EXPECT_TRUE(Widget::GetWidgetForNativeWindow(widget->GetNativeWindow()));
    bounds_ = widget->GetWindowBoundsInScreen();
  }

 private:
  gfx::Rect bounds_;
};

// Verifies Close() results in destroying.
TEST_F(DesktopWidgetTest, CloseDestroys) {
  bool destroyed = false;
  base::RunLoop run_loop;
  CloseDestroysWidget* widget =
      new CloseDestroysWidget(&destroyed, run_loop.QuitClosure());
  Widget::InitParams params =
      CreateParams(views::Widget::InitParams::TYPE_MENU);
  params.opacity = Widget::InitParams::WindowOpacity::kOpaque;
  params.bounds = gfx::Rect(50, 50, 250, 250);
  widget->Init(std::move(params));
  widget->Show();
  widget->Hide();
  widget->Close();
  EXPECT_FALSE(destroyed);
  // Run the message loop as Close() asynchronously deletes.
  run_loop.Run();
  EXPECT_TRUE(destroyed);
  // Close() should destroy the widget. If not we'll cleanup to avoid leaks.
  if (!destroyed) {
    widget->Detach();
    widget->CloseNow();
  }
}

// Tests that killing a widget while animating it does not crash.
TEST_F(WidgetTest, CloseWidgetWhileAnimating) {
  std::unique_ptr<Widget> widget = CreateTestWidget();
  AnimationEndObserver animation_observer;
  WidgetBoundsObserver widget_observer;
  gfx::Rect bounds(100, 100, 50, 50);
  {
    // Normal animations for tests have ZERO_DURATION, make sure we are actually
    // animating the movement.
    ui::ScopedAnimationDurationScaleMode animation_scale_mode(
        ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
    ui::ScopedLayerAnimationSettings animation_settings(
        widget->GetLayer()->GetAnimator());
    animation_settings.AddObserver(&animation_observer);
    widget->AddObserver(&widget_observer);
    widget->Show();

    // Animate the bounds change.
    widget->SetBounds(bounds);
    widget.reset();
    EXPECT_FALSE(animation_observer.animation_completed());
  }
  EXPECT_TRUE(animation_observer.animation_completed());
  EXPECT_EQ(widget_observer.bounds(), bounds);
}

// Test Widget::CloseAllSecondaryWidgets works as expected across platforms.
// ChromeOS doesn't implement or need CloseAllSecondaryWidgets() since
// everything is under a single root window.
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
TEST_F(DesktopWidgetTest, CloseAllSecondaryWidgets) {
  Widget* widget1 = CreateTopLevelNativeWidget();
  Widget* widget2 = CreateTopLevelNativeWidget();
  TestWidgetObserver observer1(widget1);
  TestWidgetObserver observer2(widget2);
  widget1->Show();  // Just show the first one.
  Widget::CloseAllSecondaryWidgets();
  EXPECT_TRUE(observer1.widget_closed());
  EXPECT_TRUE(observer2.widget_closed());
}
#endif

// Test that the NativeWidget is still valid during OnNativeWidgetDestroying(),
// and properties that depend on it are valid, when closed via CloseNow().
TEST_F(DesktopWidgetTest, ValidDuringOnNativeWidgetDestroyingFromCloseNow) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->Show();
  gfx::Rect screen_rect(50, 50, 100, 100);
  widget->SetBounds(screen_rect);
  WidgetBoundsObserver observer;
  widget->AddObserver(&observer);
  widget->CloseNow();
  EXPECT_EQ(screen_rect, observer.bounds());
}

// Test that the NativeWidget is still valid during OnNativeWidgetDestroying(),
// and properties that depend on it are valid, when closed via Close().
TEST_F(DesktopWidgetTest, ValidDuringOnNativeWidgetDestroyingFromClose) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->Show();
  gfx::Rect screen_rect(50, 50, 100, 100);
  widget->SetBounds(screen_rect);
  WidgetBoundsObserver observer;
  widget->AddObserver(&observer);
  widget->Close();
  EXPECT_EQ(gfx::Rect(), observer.bounds());
  base::RunLoop().RunUntilIdle();
// Broken on Linux. See http://crbug.com/515379.
// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if !(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
  EXPECT_EQ(screen_rect, observer.bounds());
#endif
}

// Tests that we do not crash when a Widget is destroyed by going out of
// scope (as opposed to being explicitly deleted by its NativeWidget).
TEST_F(WidgetTest, NoCrashOnWidgetDelete) {
  CreateTestWidget();
}

TEST_F(WidgetTest, NoCrashOnResizeConstraintsWindowTitleOnPopup) {
  CreateTestWidget(Widget::InitParams::TYPE_POPUP)->OnSizeConstraintsChanged();
}

// Tests that we do not crash when a Widget is destroyed before it finishes
// processing of pending input events in the message loop.
TEST_F(WidgetTest, NoCrashOnWidgetDeleteWithPendingEvents) {
  std::unique_ptr<Widget> widget = CreateTestWidget();
  widget->Show();

  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());
  generator->MoveMouseTo(10, 10);

// No touch on desktop Mac. Tracked in http://crbug.com/445520.
#if BUILDFLAG(IS_MAC)
  generator->ClickLeftButton();
#else
  generator->PressTouch();
#endif
  widget.reset();
}

// A view that consumes mouse-pressed event and gesture-tap-down events.
class RootViewTestView : public View {
 public:
  RootViewTestView() = default;

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

  void OnGestureEvent(ui::GestureEvent* event) override {
    if (event->type() == ui::ET_GESTURE_TAP_DOWN)
      event->SetHandled();
  }
};

// Checks if RootView::*_handler_ fields are unset when widget is hidden.
// Fails on chromium.webkit Windows bot, see crbug.com/264872.
#if BUILDFLAG(IS_WIN)
#define MAYBE_DisableTestRootViewHandlersWhenHidden \
  DISABLED_TestRootViewHandlersWhenHidden
#else
#define MAYBE_DisableTestRootViewHandlersWhenHidden \
  TestRootViewHandlersWhenHidden
#endif
TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->SetBounds(gfx::Rect(0, 0, 300, 300));
  View* view = new RootViewTestView();
  view->SetBounds(0, 0, 300, 300);
  internal::RootView* root_view =
      static_cast<internal::RootView*>(widget->GetRootView());
  root_view->AddChildView(view);

  // Check RootView::mouse_pressed_handler_.
  widget->Show();
  EXPECT_EQ(nullptr, GetMousePressedHandler(root_view));
  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);
  widget->OnMouseEvent(&press);
  EXPECT_EQ(view, GetMousePressedHandler(root_view));
  widget->Hide();
  EXPECT_EQ(nullptr, GetMousePressedHandler(root_view));

  // Check RootView::mouse_move_handler_.
  widget->Show();
  EXPECT_EQ(nullptr, GetMouseMoveHandler(root_view));
  gfx::Point move_location(45, 15);
  ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location,
                      ui::EventTimeForNow(), 0, 0);
  widget->OnMouseEvent(&move);
  EXPECT_EQ(view, GetMouseMoveHandler(root_view));
  widget->Hide();
  EXPECT_EQ(nullptr, GetMouseMoveHandler(root_view));

  // Check RootView::gesture_handler_.
  widget->Show();
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  ui::GestureEvent tap_down =
      CreateTestGestureEvent(ui::ET_GESTURE_TAP_DOWN, 15, 15);
  widget->OnGestureEvent(&tap_down);
  EXPECT_EQ(view, GetGestureHandler(root_view));
  widget->Hide();
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));

  widget->Close();
}

// Tests that the |gesture_handler_| member in RootView is always NULL
// after the dispatch of a ui::ET_GESTURE_END event corresponding to
// the release of the final touch point on the screen, but that
// ui::ET_GESTURE_END events corresponding to the removal of any other touch
// point do not modify |gesture_handler_|.
TEST_F(WidgetTest, GestureEndEvents) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->SetBounds(gfx::Rect(0, 0, 300, 300));
  EventCountView* view = new EventCountView();
  view->SetBounds(0, 0, 300, 300);
  internal::RootView* root_view =
      static_cast<internal::RootView*>(widget->GetRootView());
  root_view->AddChildView(view);
  widget->Show();

  // If no gesture handler is set, a ui::ET_GESTURE_END event should not set
  // the gesture handler.
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  ui::GestureEvent end = CreateTestGestureEvent(ui::ET_GESTURE_END, 15, 15);
  widget->OnGestureEvent(&end);
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));

  // Change the handle mode of |view| to indicate that it would like
  // to handle all events, then send a GESTURE_TAP to set the gesture handler.
  view->set_handle_mode(EventCountView::CONSUME_EVENTS);
  ui::GestureEvent tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 15, 15);
  widget->OnGestureEvent(&tap);
  EXPECT_TRUE(tap.handled());
  EXPECT_EQ(view, GetGestureHandler(root_view));

  // The gesture handler should remain unchanged on a ui::ET_GESTURE_END
  // corresponding to a second touch point, but should be reset to NULL by a
  // ui::ET_GESTURE_END corresponding to the final touch point.
  ui::GestureEventDetails details(ui::ET_GESTURE_END);
  details.set_touch_points(2);
  ui::GestureEvent end_second_touch_point =
      CreateTestGestureEvent(details, 15, 15);
  widget->OnGestureEvent(&end_second_touch_point);
  EXPECT_EQ(view, GetGestureHandler(root_view));

  end = CreateTestGestureEvent(ui::ET_GESTURE_END, 15, 15);
  widget->OnGestureEvent(&end);
  EXPECT_TRUE(end.handled());
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));

  // Send a GESTURE_TAP to set the gesture handler, then change the handle
  // mode of |view| to indicate that it does not want to handle any
  // further events.
  tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 15, 15);
  widget->OnGestureEvent(&tap);
  EXPECT_TRUE(tap.handled());
  EXPECT_EQ(view, GetGestureHandler(root_view));
  view->set_handle_mode(EventCountView::PROPAGATE_EVENTS);

  // The gesture handler should remain unchanged on a ui::ET_GESTURE_END
  // corresponding to a second touch point, but should be reset to NULL by a
  // ui::ET_GESTURE_END corresponding to the final touch point.
  end_second_touch_point = CreateTestGestureEvent(details, 15, 15);
  widget->OnGestureEvent(&end_second_touch_point);
  EXPECT_EQ(view, GetGestureHandler(root_view));

  end = CreateTestGestureEvent(ui::ET_GESTURE_END, 15, 15);
  widget->OnGestureEvent(&end);
  EXPECT_FALSE(end.handled());
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));

  widget->Close();
}

// Tests that gesture events which should not be processed (because
// RootView::OnEventProcessingStarted() has marked them as handled) are not
// dispatched to any views.
TEST_F(WidgetTest, GestureEventsNotProcessed) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->SetBounds(gfx::Rect(0, 0, 300, 300));

  // Define a hierarchy of four views (coordinates are in
  // their parent coordinate space).
  // v1 (0, 0, 300, 300)
  //   v2 (0, 0, 100, 100)
  //     v3 (0, 0, 50, 50)
  //       v4(0, 0, 10, 10)
  EventCountView* v1 = new EventCountView();
  v1->SetBounds(0, 0, 300, 300);
  EventCountView* v2 = new EventCountView();
  v2->SetBounds(0, 0, 100, 100);
  EventCountView* v3 = new EventCountView();
  v3->SetBounds(0, 0, 50, 50);
  EventCountView* v4 = new EventCountView();
  v4->SetBounds(0, 0, 10, 10);
  internal::RootView* root_view =
      static_cast<internal::RootView*>(widget->GetRootView());
  root_view->AddChildView(v1);
  v1->AddChildView(v2);
  v2->AddChildView(v3);
  v3->AddChildView(v4);

  widget->Show();

  // ui::ET_GESTURE_BEGIN events should never be seen by any view, but
  // they should be marked as handled by OnEventProcessingStarted().
  ui::GestureEvent begin = CreateTestGestureEvent(ui::ET_GESTURE_BEGIN, 5, 5);
  widget->OnGestureEvent(&begin);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_BEGIN));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_BEGIN));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_BEGIN));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_BEGIN));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(begin.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // ui::ET_GESTURE_END events should not be seen by any view when there is
  // no default gesture handler set, but they should be marked as handled by
  // OnEventProcessingStarted().
  ui::GestureEvent end = CreateTestGestureEvent(ui::ET_GESTURE_END, 5, 5);
  widget->OnGestureEvent(&end);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(end.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // ui::ET_GESTURE_END events not corresponding to the release of the
  // final touch point should never be seen by any view, but they should
  // be marked as handled by OnEventProcessingStarted().
  ui::GestureEventDetails details(ui::ET_GESTURE_END);
  details.set_touch_points(2);
  ui::GestureEvent end_second_touch_point =
      CreateTestGestureEvent(details, 5, 5);
  widget->OnGestureEvent(&end_second_touch_point);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(end_second_touch_point.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // ui::ET_GESTURE_SCROLL_UPDATE events should never be seen by any view when
  // there is no default gesture handler set, but they should be marked as
  // handled by OnEventProcessingStarted().
  ui::GestureEvent scroll_update =
      CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_UPDATE, 5, 5);
  widget->OnGestureEvent(&scroll_update);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(scroll_update.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // ui::ET_GESTURE_SCROLL_END events should never be seen by any view when
  // there is no default gesture handler set, but they should be marked as
  // handled by OnEventProcessingStarted().
  ui::GestureEvent scroll_end =
      CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_END, 5, 5);
  widget->OnGestureEvent(&scroll_end);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(scroll_end.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // ui::ET_SCROLL_FLING_START events should never be seen by any view when
  // there is no default gesture handler set, but they should be marked as
  // handled by OnEventProcessingStarted().
  ui::GestureEvent scroll_fling_start =
      CreateTestGestureEvent(ui::ET_SCROLL_FLING_START, 5, 5);
  widget->OnGestureEvent(&scroll_fling_start);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_SCROLL_FLING_START));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_SCROLL_FLING_START));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_SCROLL_FLING_START));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_SCROLL_FLING_START));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(scroll_fling_start.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  widget->Close();
}

// Tests that a (non-scroll) gesture event is dispatched to the correct views
// in a view hierarchy and that the default gesture handler in RootView is set
// correctly.
TEST_F(WidgetTest, GestureEventDispatch) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->SetBounds(gfx::Rect(0, 0, 300, 300));

  // Define a hierarchy of four views (coordinates are in
  // their parent coordinate space).
  // v1 (0, 0, 300, 300)
  //   v2 (0, 0, 100, 100)
  //     v3 (0, 0, 50, 50)
  //       v4(0, 0, 10, 10)
  EventCountView* v1 = new EventCountView();
  v1->SetBounds(0, 0, 300, 300);
  EventCountView* v2 = new EventCountView();
  v2->SetBounds(0, 0, 100, 100);
  EventCountView* v3 = new EventCountView();
  v3->SetBounds(0, 0, 50, 50);
  EventCountView* v4 = new EventCountView();
  v4->SetBounds(0, 0, 10, 10);
  internal::RootView* root_view =
      static_cast<internal::RootView*>(widget->GetRootView());
  root_view->AddChildView(v1);
  v1->AddChildView(v2);
  v2->AddChildView(v3);
  v3->AddChildView(v4);

  widget->Show();

  // No gesture handler is set in the root view and none of the views in the
  // view hierarchy handle a ui::ET_GESTURE_TAP event. In this case the tap
  // event should be dispatched to all views in the hierarchy, the gesture
  // handler should remain unset, and the event should remain unhandled.
  ui::GestureEvent tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 5, 5);
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  widget->OnGestureEvent(&tap);
  EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, v2->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_FALSE(tap.handled());

  // No gesture handler is set in the root view and |v1|, |v2|, and |v3| all
  // handle a ui::ET_GESTURE_TAP event. In this case the tap event should be
  // dispatched to |v4| and |v3|, the gesture handler should be set to |v3|,
  // and the event should be marked as handled.
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();
  v1->set_handle_mode(EventCountView::CONSUME_EVENTS);
  v2->set_handle_mode(EventCountView::CONSUME_EVENTS);
  v3->set_handle_mode(EventCountView::CONSUME_EVENTS);
  tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 5, 5);
  widget->OnGestureEvent(&tap);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(v3, GetGestureHandler(root_view));
  EXPECT_TRUE(tap.handled());

  // The gesture handler is set to |v3| and all views handle all gesture event
  // types. In this case subsequent gesture events should only be dispatched to
  // |v3| and marked as handled. The gesture handler should remain as |v3|.
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();
  v4->set_handle_mode(EventCountView::CONSUME_EVENTS);
  tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 5, 5);
  widget->OnGestureEvent(&tap);
  EXPECT_TRUE(tap.handled());
  ui::GestureEvent show_press =
      CreateTestGestureEvent(ui::ET_GESTURE_SHOW_PRESS, 5, 5);
  widget->OnGestureEvent(&show_press);
  tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 5, 5);
  widget->OnGestureEvent(&tap);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(2, v3->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_SHOW_PRESS));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SHOW_PRESS));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_SHOW_PRESS));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SHOW_PRESS));
  EXPECT_TRUE(tap.handled());
  EXPECT_TRUE(show_press.handled());
  EXPECT_EQ(v3, GetGestureHandler(root_view));

  // The gesture handler is set to |v3|, but |v3| does not handle
  // ui::ET_GESTURE_TAP events. In this case a tap gesture should be dispatched
  // only to |v3|, but the event should remain unhandled. The gesture handler
  // should remain as |v3|.
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();
  v3->set_handle_mode(EventCountView::PROPAGATE_EVENTS);
  tap = CreateTestGestureEvent(ui::ET_GESTURE_TAP, 5, 5);
  widget->OnGestureEvent(&tap);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_FALSE(tap.handled());
  EXPECT_EQ(v3, GetGestureHandler(root_view));

  widget->Close();
}

// Tests that gesture scroll events will change the default gesture handler in
// RootView if the current handler to which they are dispatched does not handle
// gesture scroll events.
TEST_F(WidgetTest, ScrollGestureEventDispatch) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->SetBounds(gfx::Rect(0, 0, 300, 300));

  // Define a hierarchy of four views (coordinates are in
  // their parent coordinate space).
  // v1 (0, 0, 300, 300)
  //   v2 (0, 0, 100, 100)
  //     v3 (0, 0, 50, 50)
  //       v4(0, 0, 10, 10)
  EventCountView* v1 = new EventCountView();
  v1->SetBounds(0, 0, 300, 300);
  EventCountView* v2 = new EventCountView();
  v2->SetBounds(0, 0, 100, 100);
  EventCountView* v3 = new EventCountView();
  v3->SetBounds(0, 0, 50, 50);
  EventCountView* v4 = new EventCountView();
  v4->SetBounds(0, 0, 10, 10);
  internal::RootView* root_view =
      static_cast<internal::RootView*>(widget->GetRootView());
  root_view->AddChildView(v1);
  v1->AddChildView(v2);
  v2->AddChildView(v3);
  v3->AddChildView(v4);

  widget->Show();

  // Change the handle mode of |v3| to indicate that it would like to handle
  // gesture events.
  v3->set_handle_mode(EventCountView::CONSUME_EVENTS);

  // When no gesture handler is set, dispatching a ui::ET_GESTURE_TAP_DOWN
  // should bubble up the views hierarchy until it reaches the first view
  // that will handle it (|v3|) and then sets the handler to |v3|.
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  ui::GestureEvent tap_down =
      CreateTestGestureEvent(ui::ET_GESTURE_TAP_DOWN, 5, 5);
  widget->OnGestureEvent(&tap_down);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP_DOWN));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP_DOWN));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP_DOWN));
  EXPECT_EQ(1, v4->GetEventCount(ui::ET_GESTURE_TAP_DOWN));
  EXPECT_EQ(v3, GetGestureHandler(root_view));
  EXPECT_TRUE(tap_down.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // A ui::ET_GESTURE_TAP_CANCEL event should be dispatched to |v3| directly.
  ui::GestureEvent tap_cancel =
      CreateTestGestureEvent(ui::ET_GESTURE_TAP_CANCEL, 5, 5);
  widget->OnGestureEvent(&tap_cancel);
  EXPECT_EQ(0, v1->GetEventCount(ui::ET_GESTURE_TAP_CANCEL));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_TAP_CANCEL));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_TAP_CANCEL));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_TAP_CANCEL));
  EXPECT_EQ(v3, GetGestureHandler(root_view));
  EXPECT_TRUE(tap_cancel.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // Change the handle mode of |v3| to indicate that it would no longer like
  // to handle events, and change the mode of |v1| to indicate that it would
  // like to handle events.
  v3->set_handle_mode(EventCountView::PROPAGATE_EVENTS);
  v1->set_handle_mode(EventCountView::CONSUME_EVENTS);

  // Dispatch a ui::ET_GESTURE_SCROLL_BEGIN event. Because the current gesture
  // handler (|v3|) does not handle scroll events, the event should bubble up
  // the views hierarchy until it reaches the first view that will handle
  // it (|v1|) and then sets the handler to |v1|.
  ui::GestureEvent scroll_begin =
      CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN, 5, 5);
  widget->OnGestureEvent(&scroll_begin);
  EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
  EXPECT_EQ(1, v2->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
  EXPECT_EQ(1, v3->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
  EXPECT_EQ(v1, GetGestureHandler(root_view));
  EXPECT_TRUE(scroll_begin.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // A ui::ET_GESTURE_SCROLL_UPDATE event should be dispatched to |v1|
  // directly.
  ui::GestureEvent scroll_update =
      CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_UPDATE, 5, 5);
  widget->OnGestureEvent(&scroll_update);
  EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
  EXPECT_EQ(v1, GetGestureHandler(root_view));
  EXPECT_TRUE(scroll_update.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // A ui::ET_GESTURE_SCROLL_END event should be dispatched to |v1|
  // directly and should not reset the gesture handler.
  ui::GestureEvent scroll_end =
      CreateTestGestureEvent(ui::ET_GESTURE_SCROLL_END, 5, 5);
  widget->OnGestureEvent(&scroll_end);
  EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_SCROLL_END));
  EXPECT_EQ(v1, GetGestureHandler(root_view));
  EXPECT_TRUE(scroll_end.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // A ui::ET_GESTURE_PINCH_BEGIN event (which is a non-scroll event) should
  // still be dispatched to |v1| directly.
  ui::GestureEvent pinch_begin =
      CreateTestGestureEvent(ui::ET_GESTURE_PINCH_BEGIN, 5, 5);
  widget->OnGestureEvent(&pinch_begin);
  EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_PINCH_BEGIN));
  EXPECT_EQ(v1, GetGestureHandler(root_view));
  EXPECT_TRUE(pinch_begin.handled());
  v1->ResetCounts();
  v2->ResetCounts();
  v3->ResetCounts();
  v4->ResetCounts();

  // A ui::ET_GESTURE_END event should be dispatched to |v1| and should
  // set the gesture handler to NULL.
  ui::GestureEvent end = CreateTestGestureEvent(ui::ET_GESTURE_END, 5, 5);
  widget->OnGestureEvent(&end);
  EXPECT_EQ(1, v1->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v2->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v3->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(0, v4->GetEventCount(ui::ET_GESTURE_END));
  EXPECT_EQ(nullptr, GetGestureHandler(root_view));
  EXPECT_TRUE(end.handled());

  widget->Close();
}

// TODO(b/271490637): on Mac a drag controller should still be notified when
// drag will start. Figure out how to write a unit test for Mac. Then remove
// this build flag check.
#if !BUILDFLAG(IS_MAC)

// Verifies that the drag controller is notified when the view drag will start.
TEST_F(WidgetTest, NotifyDragControllerWhenDragWillStart) {
  // Create a widget whose contents view is draggable.
  UniqueWidgetPtr widget(std::make_unique<Widget>());
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
  params.bounds = gfx::Rect(/*width=*/650, /*height=*/650);
  widget->Init(std::move(params));
  widget->Show();
  MockDragController mock_drag_controller;
  views::View contents_view;
  contents_view.set_drag_controller(&mock_drag_controller);
  widget->SetContentsView(&contents_view);

  // Expect the drag controller is notified of the drag start.
  EXPECT_CALL(mock_drag_controller, OnWillStartDragForView(&contents_view));

  // Drag-and-drop `contents_view` by mouse.
  ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow());
  generator.MoveMouseTo(contents_view.GetBoundsInScreen().CenterPoint());
  generator.PressLeftButton();
  generator.MoveMouseBy(/*x=*/200, /*y=*/0);
  generator.ReleaseLeftButton();
}

#endif  // !BUILDFLAG(IS_MAC)

// A class used in WidgetTest.GestureEventLocationWhileBubbling to verify
// that when a gesture event bubbles up a View hierarchy, the location
// of a gesture event seen by each View is in the local coordinate space
// of that View.
class GestureLocationView : public EventCountView {
 public:
  GestureLocationView() = default;

  GestureLocationView(const GestureLocationView&) = delete;
  GestureLocationView& operator=(const GestureLocationView&) = delete;

  ~GestureLocationView() override = default;

  void set_expected_location(gfx::Point expected_location) {
    expected_location_ = expected_location;
  }

  // EventCountView:
  void OnGestureEvent(ui::GestureEvent* event) override {
    EventCountView::OnGestureEvent(event);

    // Verify that the location of |event| is in the local coordinate
    // space of |this|.
    EXPECT_EQ(expected_location_, event->location());
  }

 private:
  // The expected location of a gesture event dispatched to |this|.
  gfx::Point expected_location_;
};

// Verifies that the location of a gesture event is always in the local
// coordinate space of the View receiving the event while bubbling.
TEST_F(WidgetTest, GestureEventLocationWhileBubbling) {
  Widget* widget = CreateTopLevelNativeWidget();
  widget->SetBounds(gfx::Rect(0, 0, 300, 300));

  // Define a hierarchy of three views (coordinates shown below are in the
  // coordinate space of the root view, but the coordinates used for
  // SetBounds() are in their parent coordinate space).
  // v1 (50, 50, 150, 150)
  //   v2 (100, 70, 50, 80)
  //     v3 (120, 100, 10, 10)
  GestureLocationView* v1 = new GestureLocationView();
  v1->SetBounds(50, 50, 150, 150);
  GestureLocationView* v2 = new GestureLocationView();
  v2->SetBounds(50, 20, 50, 80);
  GestureLocationView* v3 = new GestureLocationView();
  v3->SetBounds(20, 30, 10, 10);
  internal::RootView* root_view =
      static_cast<internal::RootView*>(widget->GetRootView());
  root_view->AddChildView(v1);
  v1->AddChildView(v2);
  v2->AddChildView(v3);

  widget->Show();

  // Define a GESTURE_TAP event located at (125, 105) in root view coordinates.
  // This event is contained within all of |v1|, |v2|, and |v3|.
  gfx::Point location_in_root(125, 105);
  ui::GestureEvent tap = CreateTestGestureEvent(
      ui::ET_GESTURE_TAP, location_in_root.x(), location_in_root.y());

  // Calculate the location of the event in the local coordinate spaces
  // of each of the views.
  gfx::Point location_in_v1(ConvertPointFromWidgetToView(v1, location_in_root));
  EXPECT_EQ(gfx::Point(75, 55), location_in_v1);
  gfx::Point location_in_v2(ConvertPointFromWidgetToView(v2, location_in_root));
  EXPECT_EQ(gfx::Point(25, 35), location_in_v2);
  gfx::Point location_in_v3(ConvertPointFromWidgetToView(v3, location_in_root));
  EXPECT_EQ(gfx::Point(5, 5), location_in_v3);

  // Dispatch the event. When each view receives the event, its location should
  // be in the local coordinate space of that view (see the check made by
  // GestureLocationView). After dispatch is complete the event's location
  // should be in the root coordinate space.
  v1->set_expected_location(location_in_v1);
  v2->set_expected_location(location_in_v2);
  v3->set_expected_location(location_in_v3);
  widget->OnGestureEvent(&tap);
  EXPECT_EQ(location_in_root, tap.location());

  // Verify that each view did in fact see the event.
  EventCountView* view1 = v1;
  EventCountView* view2 = v2;
  EventCountView* view3 = v3;
  EXPECT_EQ(1, view1->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, view2->GetEventCount(ui::ET_GESTURE_TAP));
  EXPECT_EQ(1, view3->GetEventCount(ui::ET_GESTURE_TAP));

  widget->Close();
}

// Test the result of Widget::GetAllChildWidgets().
TEST_F(WidgetTest, GetAllChildWidgets) {
  // Create the following widget hierarchy:
  //
  // toplevel
  // +-- w1
  //     +-- w11
  // +-- w2
  //     +-- w21
  //     +-- w22
  WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
  Widget* w1 = CreateChildPlatformWidget(toplevel->GetNativeView());
  Widget* w11 = CreateChildPlatformWidget(w1->GetNativeView());
  Widget* w2 = CreateChildPlatformWidget(toplevel->GetNativeView());
  Widget* w21 = CreateChildPlatformWidget(w2->GetNativeView());
  Widget* w22 = CreateChildPlatformWidget(w2->GetNativeView());

  std::set<Widget*> expected;
  expected.insert(toplevel.get());
  expected.insert(w1);
  expected.insert(w11);
  expected.insert(w2);
  expected.insert(w21);
  expected.insert(w22);

  std::set<Widget*> child_widgets;
  Widget::GetAllChildWidgets(toplevel->GetNativeView(), &child_widgets);

  EXPECT_TRUE(base::ranges::equal(expected, child_widgets));

  // Check GetAllOwnedWidgets(). On Aura, this includes "transient" children.
  // Otherwise (on all platforms), it should be the same as GetAllChildWidgets()
  // except the root Widget is not included.
  EXPECT_EQ(1u, expected.erase(toplevel.get()));

  std::set<Widget*> owned_widgets;
  Widget::GetAllOwnedWidgets(toplevel->GetNativeView(), &owned_widgets);

  EXPECT_TRUE(base::ranges::equal(expected, owned_widgets));
}

// Used by DestroyChildWidgetsInOrder. On destruction adds the supplied name to
// a vector.
class DestroyedTrackingView : public View {
 public:
  DestroyedTrackingView(const std::string& name,
                        std::vector<std::string>* add_to)
      : name_(name), add_to_(add_to) {}

  DestroyedTrackingView(const DestroyedTrackingView&) = delete;
  DestroyedTrackingView& operator=(const DestroyedTrackingView&) = delete;

  ~DestroyedTrackingView() override { add_to_->push_back(name_); }

 private:
  const std::string name_;
  raw_ptr<std::vector<std::string>> add_to_;
};

class WidgetChildDestructionTest : public DesktopWidgetTest {
 public:
  WidgetChildDestructionTest() = default;

  WidgetChildDestructionTest(const WidgetChildDestructionTest&) = delete;
  WidgetChildDestructionTest& operator=(const WidgetChildDestructionTest&) =
      delete;

  // Creates a top level and a child, destroys the child and verifies the views
  // of the child are destroyed before the views of the parent.
  void RunDestroyChildWidgetsTest(bool top_level_has_desktop_native_widget_aura,
                                  bool child_has_desktop_native_widget_aura) {
    // When a View is destroyed its name is added here.
    std::vector<std::string> destroyed;

    Widget* top_level = new Widget;
    Widget::InitParams params =
        CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    if (!top_level_has_desktop_native_widget_aura) {
      params.native_widget =
          CreatePlatformNativeWidgetImpl(top_level, kStubCapture, nullptr);
    }
    top_level->Init(std::move(params));
    top_level->GetRootView()->AddChildView(
        new DestroyedTrackingView("parent", &destroyed));
    top_level->Show();

    Widget* child = new Widget;
    Widget::InitParams child_params =
        CreateParams(views::Widget::InitParams::TYPE_POPUP);
    child_params.parent = top_level->GetNativeView();
    if (!child_has_desktop_native_widget_aura) {
      child_params.native_widget =
          CreatePlatformNativeWidgetImpl(child, kStubCapture, nullptr);
    }
    child->Init(std::move(child_params));
    child->GetRootView()->AddChildView(
        new DestroyedTrackingView("child", &destroyed));
    child->Show();

    // Should trigger destruction of the child too.
    top_level->native_widget_private()->CloseNow();

    // Child should be destroyed first.
    ASSERT_EQ(2u, destroyed.size());
    EXPECT_EQ("child", destroyed[0]);
    EXPECT_EQ("parent", destroyed[1]);
  }
};

// See description of RunDestroyChildWidgetsTest(). Parent uses
// DesktopNativeWidgetAura.
TEST_F(WidgetChildDestructionTest,
       DestroyChildWidgetsInOrderWithDesktopNativeWidget) {
  RunDestroyChildWidgetsTest(true, false);
}

// See description of RunDestroyChildWidgetsTest(). Both parent and child use
// DesktopNativeWidgetAura.
TEST_F(WidgetChildDestructionTest,
       DestroyChildWidgetsInOrderWithDesktopNativeWidgetForBoth) {
  RunDestroyChildWidgetsTest(true, true);
}

// See description of RunDestroyChildWidgetsTest().
TEST_F(WidgetChildDestructionTest, DestroyChildWidgetsInOrder) {
  RunDestroyChildWidgetsTest(false, false);
}

// Verifies nativeview visbility matches that of Widget visibility when
// SetFullscreen is invoked.
TEST_F(WidgetTest, FullscreenStatePropagated) {
  std::unique_ptr<Widget> top_level_widget = CreateTestWidget();
  top_level_widget->SetFullscreen(true);
  EXPECT_EQ(top_level_widget->IsVisible(),
            IsNativeWindowVisible(top_level_widget->GetNativeWindow()));
}

// Verifies nativeview visbility matches that of Widget visibility when
// SetFullscreen is invoked, for a widget provided with a desktop widget.
TEST_F(DesktopWidgetTest, FullscreenStatePropagated_DesktopWidget) {
  std::unique_ptr<Widget> top_level_widget = CreateTestWidget();
  top_level_widget->SetFullscreen(true);
  EXPECT_EQ(top_level_widget->IsVisible(),
            IsNativeWindowVisible(top_level_widget->GetNativeWindow()));
}

// Used to delete the widget when the supplied bounds changes.
class DestroyingWidgetBoundsObserver : public WidgetObserver {
 public:
  explicit DestroyingWidgetBoundsObserver(std::unique_ptr<Widget> widget)
      : widget_(std::move(widget)) {
    widget_->AddObserver(this);
  }

  // There are no assertions here as not all platforms call
  // OnWidgetBoundsChanged() when going fullscreen.
  ~DestroyingWidgetBoundsObserver() override = default;

  // WidgetObserver:
  void OnWidgetBoundsChanged(Widget* widget,
                             const gfx::Rect& new_bounds) override {
    widget_->RemoveObserver(this);
    widget_.reset();
  }

 private:
  std::unique_ptr<Widget> widget_;
};

// Deletes a Widget when the bounds change as part of toggling fullscreen.
// This is a regression test for https://crbug.com/1197436.
// Disabled on Mac: This test has historically deleted the Widget not during
// SetFullscreen, but at the end of the test. When the Widget is deleted inside
// SetFullscreen, the test crashes.
// https://crbug.com/1307486
#if BUILDFLAG(IS_MAC)
#define MAYBE_DeleteInSetFullscreen DISABLED_DeleteInSetFullscreen
#else
#define MAYBE_DeleteInSetFullscreen DeleteInSetFullscreen
#endif
TEST_F(DesktopWidgetTest, MAYBE_DeleteInSetFullscreen) {
  std::unique_ptr<Widget> widget = std::make_unique<Widget>();
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget->Init(std::move(params));
  Widget* w = widget.get();
  DestroyingWidgetBoundsObserver destroyer(std::move(widget));
  w->SetFullscreen(true);
}

namespace {

class FullscreenAwareFrame : public views::NonClientFrameView {
 public:
  explicit FullscreenAwareFrame(views::Widget* widget) : widget_(widget) {}

  FullscreenAwareFrame(const FullscreenAwareFrame&) = delete;
  FullscreenAwareFrame& operator=(const FullscreenAwareFrame&) = delete;

  ~FullscreenAwareFrame() override = default;

  // views::NonClientFrameView overrides:
  gfx::Rect GetBoundsForClientView() const override { return gfx::Rect(); }
  gfx::Rect GetWindowBoundsForClientBounds(
      const gfx::Rect& client_bounds) const override {
    return gfx::Rect();
  }
  int NonClientHitTest(const gfx::Point& point) override { return HTNOWHERE; }
  void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override {}
  void ResetWindowControls() override {}
  void UpdateWindowIcon() override {}
  void UpdateWindowTitle() override {}
  void SizeConstraintsChanged() override {}

  // views::View overrides:
  void Layout() override {
    if (widget_->IsFullscreen())
      fullscreen_layout_called_ = true;
  }

  bool fullscreen_layout_called() const { return fullscreen_layout_called_; }

 private:
  raw_ptr<views::Widget> widget_;
  bool fullscreen_layout_called_ = false;
};

}  // namespace

// Tests that frame Layout is called when a widget goes fullscreen without
// changing its size or title.
TEST_F(WidgetTest, FullscreenFrameLayout) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  auto frame_view = std::make_unique<FullscreenAwareFrame>(widget.get());
  FullscreenAwareFrame* frame = frame_view.get();
  widget->non_client_view()->SetFrameView(std::move(frame_view));

  widget->Maximize();
  RunPendingMessages();

  EXPECT_FALSE(frame->fullscreen_layout_called());
  widget->SetFullscreen(true);
  widget->Show();
#if BUILDFLAG(IS_MAC)
  // On macOS, a fullscreen layout is triggered from within SetFullscreen.
  // https://crbug.com/1307496
  EXPECT_TRUE(frame->fullscreen_layout_called());
#else
  EXPECT_TRUE(ViewTestApi(frame).needs_layout());
#endif
  widget->LayoutRootViewIfNecessary();
  RunPendingMessages();

  EXPECT_TRUE(frame->fullscreen_layout_called());
}

namespace {

// Trivial WidgetObserverTest that invokes Widget::IsActive() from
// OnWindowDestroying.
class IsActiveFromDestroyObserver : public WidgetObserver {
 public:
  IsActiveFromDestroyObserver() = default;

  IsActiveFromDestroyObserver(const IsActiveFromDestroyObserver&) = delete;
  IsActiveFromDestroyObserver& operator=(const IsActiveFromDestroyObserver&) =
      delete;

  ~IsActiveFromDestroyObserver() override = default;
  void OnWidgetDestroying(Widget* widget) override { widget->IsActive(); }
};

}  // namespace

class ChildDesktopWidgetTest : public DesktopWidgetTest {
 public:
  Widget::InitParams CreateParams(Widget::InitParams::Type type) override {
    Widget::InitParams params = DesktopWidgetTest::CreateParams(type);
    if (context_)
      params.context = context_;
    return params;
  }

  std::unique_ptr<Widget> CreateChildWidget(gfx::NativeWindow context) {
    context_ = context;
    return CreateTestWidget();
  }

 private:
  gfx::NativeWindow context_ = nullptr;
};

// Verifies Widget::IsActive() invoked from
// WidgetObserver::OnWidgetDestroying() in a child widget doesn't crash.
TEST_F(ChildDesktopWidgetTest, IsActiveFromDestroy) {
  // Create two widgets, one a child of the other.
  IsActiveFromDestroyObserver observer;
  std::unique_ptr<Widget> parent_widget = CreateTestWidget();
  parent_widget->Show();

  std::unique_ptr<Widget> child_widget =
      CreateChildWidget(parent_widget->GetNativeWindow());
  child_widget->AddObserver(&observer);
  child_widget->Show();

  parent_widget->CloseNow();
}

// Tests that events propagate through from the dispatcher with the correct
// event type, and that the different platforms behave the same.
TEST_F(WidgetTest, MouseEventTypesViaGenerator) {
  WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget());
  EventCountView* view =
      widget->GetRootView()->AddChildView(std::make_unique<EventCountView>());
  view->set_handle_mode(EventCountView::CONSUME_EVENTS);
  view->SetBounds(10, 10, 50, 40);

  widget->SetBounds(gfx::Rect(0, 0, 100, 80));
  widget->Show();

  auto generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());
  const gfx::Point view_center_point = view->GetBoundsInScreen().CenterPoint();
  generator->set_current_screen_location(view_center_point);

  generator->ClickLeftButton();
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_RELEASED));
  EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, view->last_flags());

  generator->PressRightButton();
  EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_RELEASED));
  EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, view->last_flags());

  generator->ReleaseRightButton();
  EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_RELEASED));
  EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, view->last_flags());

  // Test mouse move events.
  EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSE_ENTERED));

  // Move the mouse a displacement of (10, 10).
  generator->MoveMouseTo(view_center_point + gfx::Vector2d(10, 10));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_ENTERED));
  EXPECT_EQ(ui::EF_NONE, view->last_flags());

  // Move it again - entered count shouldn't change.
  generator->MoveMouseTo(view_center_point + gfx::Vector2d(11, 11));
  EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_ENTERED));
  EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSE_EXITED));

  // Move it off the view.
  const gfx::Point out_of_bounds_point =
      view->GetBoundsInScreen().bottom_right() + gfx::Vector2d(10, 10);
  generator->MoveMouseTo(out_of_bounds_point);
  EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_ENTERED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_EXITED));

  // Move it back on.
  generator->MoveMouseTo(view_center_point);
  EXPECT_EQ(3, view->GetEventCount(ui::ET_MOUSE_MOVED));
  EXPECT_EQ(2, view->GetEventCount(ui::ET_MOUSE_ENTERED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_EXITED));

  // Drargging. Cover HasCapture() and NativeWidgetPrivate::IsMouseButtonDown().
  generator->DragMouseTo(out_of_bounds_point);
  EXPECT_EQ(3, view->GetEventCount(ui::ET_MOUSE_PRESSED));
  EXPECT_EQ(3, view->GetEventCount(ui::ET_MOUSE_RELEASED));
  EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSE_DRAGGED));
  EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, view->last_flags());
}

// Tests that the root view is correctly set up for Widget types that do not
// require a non-client view, before any other views are added to the widget.
// That is, before Widget::ReorderNativeViews() is called which, if called with
// a root view not set, could cause the root view to get resized to the widget.
TEST_F(WidgetTest, NonClientWindowValidAfterInit) {
  WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget());
  View* root_view = widget->GetRootView();

  // Size the root view to exceed the widget bounds.
  const gfx::Rect test_rect(0, 0, 500, 500);
  root_view->SetBoundsRect(test_rect);

  EXPECT_NE(test_rect.size(), widget->GetWindowBoundsInScreen().size());

  EXPECT_EQ(test_rect, root_view->bounds());
  widget->ReorderNativeViews();
  EXPECT_EQ(test_rect, root_view->bounds());
}

#if BUILDFLAG(IS_WIN)
// Provides functionality to subclass a window and keep track of messages
// received.
class SubclassWindowHelper {
 public:
  explicit SubclassWindowHelper(HWND window)
      : window_(window), message_to_destroy_on_(0) {
    EXPECT_EQ(instance_, nullptr);
    instance_ = this;
    EXPECT_TRUE(Subclass());
  }

  SubclassWindowHelper(const SubclassWindowHelper&) = delete;
  SubclassWindowHelper& operator=(const SubclassWindowHelper&) = delete;

  ~SubclassWindowHelper() {
    Unsubclass();
    instance_ = nullptr;
  }

  // Returns true if the |message| passed in was received.
  bool received_message(unsigned int message) {
    return (messages_.find(message) != messages_.end());
  }

  void Clear() { messages_.clear(); }

  void set_message_to_destroy_on(unsigned int message) {
    message_to_destroy_on_ = message;
  }

 private:
  bool Subclass() {
    old_proc_ = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(
        window_, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WndProc)));
    return old_proc_ != nullptr;
  }

  void Unsubclass() {
    ::SetWindowLongPtr(window_, GWLP_WNDPROC,
                       reinterpret_cast<LONG_PTR>(old_proc_));
  }

  static LRESULT CALLBACK WndProc(HWND window,
                                  unsigned int message,
                                  WPARAM w_param,
                                  LPARAM l_param) {
    EXPECT_NE(instance_, nullptr);
    EXPECT_EQ(window, instance_->window_);

    // Keep track of messags received for this window.
    instance_->messages_.insert(message);

    LRESULT ret = ::CallWindowProc(instance_->old_proc_, window, message,
                                   w_param, l_param);
    if (message == instance_->message_to_destroy_on_) {
      instance_->Unsubclass();
      ::DestroyWindow(window);
    }
    return ret;
  }

  WNDPROC old_proc_;
  HWND window_;
  static SubclassWindowHelper* instance_;
  std::set<unsigned int> messages_;
  unsigned int message_to_destroy_on_;
};

SubclassWindowHelper* SubclassWindowHelper::instance_ = nullptr;

// This test validates whether the WM_SYSCOMMAND message for SC_MOVE is
// received when we post a WM_NCLBUTTONDOWN message for the caption in the
// following scenarios:-
// 1. Posting a WM_NCMOUSEMOVE message for a different location.
// 2. Posting a WM_NCMOUSEMOVE message with a different hittest code.
// 3. Posting a WM_MOUSEMOVE message.
// Disabled because of flaky timeouts: http://crbug.com/592742
TEST_F(DesktopWidgetTest,
       DISABLED_SysCommandMoveOnNCLButtonDownOnCaptionAndMoveTest) {
  std::unique_ptr<Widget> widget =
      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
  widget->Show();
  ::SetCursorPos(500, 500);

  HWND window = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();

  SubclassWindowHelper subclass_helper(window);

  // Posting just a WM_NCLBUTTONDOWN message should not result in a
  // WM_SYSCOMMAND
  ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100));
  RunPendingMessages();
  EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN));
  EXPECT_FALSE(subclass_helper.received_message(WM_SYSCOMMAND));

  subclass_helper.Clear();
  // Posting a WM_NCLBUTTONDOWN message followed by a WM_NCMOUSEMOVE at the
  // same location should not result in a WM_SYSCOMMAND message.
  ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100));
  ::PostMessage(window, WM_NCMOUSEMOVE, HTCAPTION, MAKELPARAM(100, 100));
  RunPendingMessages();

  EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN));
  EXPECT_TRUE(subclass_helper.received_message(WM_NCMOUSEMOVE));
  EXPECT_FALSE(subclass_helper.received_message(WM_SYSCOMMAND));

  subclass_helper.Clear();
  // Posting a WM_NCLBUTTONDOWN message followed by a WM_NCMOUSEMOVE at a
  // different location should result in a WM_SYSCOMMAND message.
  ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100));
  ::PostMessage(window, WM_NCMOUSEMOVE, HTCAPTION, MAKELPARAM(110, 110));
  RunPendingMessages();

  EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN));
  EXPECT_TRUE(subclass_helper.received_message(WM_NCMOUSEMOVE));
  EXPECT_TRUE(subclass_helper.received_message(WM_SYSCOMMAND));

  subclass_helper.Clear();
  // Posting a WM_NCLBUTTONDOWN message followed by a WM_NCMOUSEMOVE at a
  // different location with a different hittest code should result in a
  // WM_SYSCOMMAND message.
  ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100));
  ::PostMessage(window, WM_NCMOUSEMOVE, HTTOP, MAKELPARAM(110, 102));
  RunPendingMessages();

  EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN));
  EXPECT_TRUE(subclass_helper.received_message(WM_NCMOUSEMOVE));
  EXPECT_TRUE(subclass_helper.received_message(WM_SYSCOMMAND));

  subclass_helper.Clear();
  // Posting a WM_NCLBUTTONDOWN message followed by a WM_MOUSEMOVE should
  // result in a WM_SYSCOMMAND message.
  ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100));
  ::PostMessage(window, WM_MOUSEMOVE, HTCLIENT, MAKELPARAM(110, 110));
  RunPendingMessages();

  EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN));
  EXPECT_TRUE(subclass_helper.received_message(WM_MOUSEMOVE));
  EXPECT_TRUE(subclass_helper.received_message(WM_SYSCOMMAND));
}

// This test validates that destroying the window in the context of the
// WM_SYSCOMMAND message with SC_MOVE does not crash.
// Disabled because of flaky timeouts: http://crbug.com/592742
TEST_F(DesktopWidgetTest, DISABLED_DestroyInSysCommandNCLButtonDownOnCaption) {
  std::unique_ptr<Widget> widget =
      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
  widget->Show();
  ::SetCursorPos(500, 500);

  HWND window = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();

  SubclassWindowHelper subclass_helper(window);

  // Destroying the window in the context of the WM_SYSCOMMAND message
  // should not crash.
  subclass_helper.set_message_to_destroy_on(WM_SYSCOMMAND);

  ::PostMessage(window, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(100, 100));
  ::PostMessage(window, WM_NCMOUSEMOVE, HTCAPTION, MAKELPARAM(110, 110));
  RunPendingMessages();

  EXPECT_TRUE(subclass_helper.received_message(WM_NCLBUTTONDOWN));
  EXPECT_TRUE(subclass_helper.received_message(WM_SYSCOMMAND));
}

#endif

// Test that the z-order levels round-trip.
TEST_F(WidgetTest, ZOrderLevel) {
  WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
  EXPECT_EQ(ui::ZOrderLevel::kNormal, widget->GetZOrderLevel());
  widget->SetZOrderLevel(ui::ZOrderLevel::kFloatingWindow);
  EXPECT_EQ(ui::ZOrderLevel::kFloatingWindow, widget->GetZOrderLevel());
  widget->SetZOrderLevel(ui::ZOrderLevel::kNormal);
  EXPECT_EQ(ui::ZOrderLevel::kNormal, widget->GetZOrderLevel());
}

namespace {

class ScaleFactorView : public View {
 public:
  ScaleFactorView() = default;

  ScaleFactorView(const ScaleFactorView&) = delete;
  ScaleFactorView& operator=(const ScaleFactorView&) = delete;

  // Overridden from ui::LayerDelegate:
  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                  float new_device_scale_factor) override {
    last_scale_factor_ = new_device_scale_factor;
    View::OnDeviceScaleFactorChanged(old_device_scale_factor,
                                     new_device_scale_factor);
  }

  float last_scale_factor() const { return last_scale_factor_; }

 private:
  float last_scale_factor_ = 0.f;
};

}  // namespace

// Ensure scale factor changes are propagated from the native Widget.
TEST_F(WidgetTest, OnDeviceScaleFactorChanged) {
  // Automatically close the widget, but not delete it.
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  ScaleFactorView* view = new ScaleFactorView;
  widget->GetRootView()->AddChildView(view);
  float scale_factor = widget->GetLayer()->device_scale_factor();
  EXPECT_NE(scale_factor, 0.f);

  // For views that are not layer-backed, adding the view won't notify the view
  // about the initial scale factor. Fake it.
  view->OnDeviceScaleFactorChanged(0.f, scale_factor);
  EXPECT_EQ(scale_factor, view->last_scale_factor());

  // Changes should be propagated.
  scale_factor *= 2.0f;
  widget->GetLayer()->OnDeviceScaleFactorChanged(scale_factor);
  EXPECT_EQ(scale_factor, view->last_scale_factor());
}

// Test that WidgetRemovalsObserver::OnWillRemoveView is called when deleting
// a view.
TEST_F(WidgetTest, WidgetRemovalsObserverCalled) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  TestWidgetRemovalsObserver removals_observer;
  widget->AddRemovalsObserver(&removals_observer);

  View* parent = new View();
  widget->client_view()->AddChildView(parent);

  View* child = new View();
  parent->AddChildView(child);

  widget->client_view()->RemoveChildView(parent);
  EXPECT_TRUE(removals_observer.DidRemoveView(parent));
  EXPECT_FALSE(removals_observer.DidRemoveView(child));

  // Calling RemoveChildView() doesn't delete the view, but deleting
  // |parent| will automatically delete |child|.
  delete parent;

  widget->RemoveRemovalsObserver(&removals_observer);
}

// Test that WidgetRemovalsObserver::OnWillRemoveView is called when deleting
// the root view.
TEST_F(WidgetTest, WidgetRemovalsObserverCalledWhenRemovingRootView) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  TestWidgetRemovalsObserver removals_observer;
  widget->AddRemovalsObserver(&removals_observer);
  views::View* root_view = widget->GetRootView();

  widget.reset();
  EXPECT_TRUE(removals_observer.DidRemoveView(root_view));
}

// Test that WidgetRemovalsObserver::OnWillRemoveView is called when moving
// a view from one widget to another, but not when moving a view within
// the same widget.
TEST_F(WidgetTest, WidgetRemovalsObserverCalledWhenMovingBetweenWidgets) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  TestWidgetRemovalsObserver removals_observer;
  widget->AddRemovalsObserver(&removals_observer);

  View* parent = new View();
  widget->client_view()->AddChildView(parent);

  View* child = new View();
  widget->client_view()->AddChildView(child);

  // Reparenting the child shouldn't call the removals observer.
  parent->AddChildView(child);
  EXPECT_FALSE(removals_observer.DidRemoveView(child));

  // Moving the child to a different widget should call the removals observer.
  WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget());
  widget2->client_view()->AddChildView(child);
  EXPECT_TRUE(removals_observer.DidRemoveView(child));

  widget->RemoveRemovalsObserver(&removals_observer);
}

// Test dispatch of ui::ET_MOUSEWHEEL.
TEST_F(WidgetTest, MouseWheelEvent) {
  WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
  widget->SetBounds(gfx::Rect(0, 0, 600, 600));
  EventCountView* event_count_view =
      widget->client_view()->AddChildView(std::make_unique<EventCountView>());
  event_count_view->SetBounds(0, 0, 600, 600);
  widget->Show();

  auto event_generator =
      CreateEventGenerator(GetContext(), widget->GetNativeWindow());

  event_generator->MoveMouseWheel(1, 1);
  EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSEWHEEL));
}

class CloseFromClosingObserver : public WidgetObserver {
 public:
  ~CloseFromClosingObserver() override {
    EXPECT_TRUE(was_on_widget_closing_called_);
  }
  // WidgetObserver:
  void OnWidgetClosing(Widget* widget) override {
    // OnWidgetClosing() should only be called once, even if Close() is called
    // after CloseNow().
    ASSERT_FALSE(was_on_widget_closing_called_);
    was_on_widget_closing_called_ = true;
    widget->Close();
  }

 private:
  bool was_on_widget_closing_called_ = false;
};

TEST_F(WidgetTest, CloseNowFollowedByCloseDoesntCallOnWidgetClosingTwice) {
  CloseFromClosingObserver observer;
  std::unique_ptr<Widget> widget = std::make_unique<Widget>();
  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  widget->Init(std::move(params));
  widget->AddObserver(&observer);
  widget->CloseNow();
  widget->RemoveObserver(&observer);
  widget.reset();
  // Assertions are in CloseFromClosingObserver.
}

namespace {

class TestSaveWindowPlacementWidgetDelegate : public TestDesktopWidgetDelegate {
 public:
  TestSaveWindowPlacementWidgetDelegate() = default;
  TestSaveWindowPlacementWidgetDelegate(
      const TestSaveWindowPlacementWidgetDelegate&) = delete;
  TestSaveWindowPlacementWidgetDelegate operator=(
      const TestSaveWindowPlacementWidgetDelegate&) = delete;
  ~TestSaveWindowPlacementWidgetDelegate() override = default;

  void set_should_save_window_placement(bool should_save) {
    should_save_window_placement_ = should_save;
  }

  int save_window_placement_count() const {
    return save_window_placement_count_;
  }

  // ViewsDelegate:
  std::string GetWindowName() const final { return GetWidget()->GetName(); }
  bool ShouldSaveWindowPlacement() const final {
    return should_save_window_placement_;
  }
  void SaveWindowPlacement(const gfx::Rect& bounds,
                           ui::WindowShowState show_state) override {
    save_window_placement_count_++;
  }

 private:
  bool should_save_window_placement_ = true;
  int save_window_placement_count_ = 0;
};

}  // namespace

TEST_F(WidgetTest, ShouldSaveWindowPlacement) {
  for (bool save : {false, true}) {
    SCOPED_TRACE(save ? "ShouldSave" : "ShouldNotSave");
    TestSaveWindowPlacementWidgetDelegate widget_delegate;
    widget_delegate.set_should_save_window_placement(save);
    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    params.name = "TestWidget";
    widget_delegate.InitWidget(std::move(params));
    auto* widget = widget_delegate.GetWidget();
    widget->Close();
    EXPECT_EQ(save ? 1 : 0, widget_delegate.save_window_placement_count());
  }
}

// Parameterized test that verifies the behavior of SetAspectRatio with respect
// to the excluded margin.
class WidgetSetAspectRatioTest
    : public ViewsTestBase,
      public testing::WithParamInterface<gfx::Size /* margin */> {
 public:
  WidgetSetAspectRatioTest() : margin_(GetParam()) {}

  WidgetSetAspectRatioTest(const WidgetSetAspectRatioTest&) = delete;
  WidgetSetAspectRatioTest& operator=(const WidgetSetAspectRatioTest&) = delete;

  ~WidgetSetAspectRatioTest() override = default;

  // ViewsTestBase:
  void SetUp() override {
    ViewsTestBase::SetUp();
    widget_ = std::make_unique<Widget>();
    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
    native_widget_ = std::make_unique<MockNativeWidget>(widget());
    ON_CALL(*native_widget(), CreateNonClientFrameView).WillByDefault([this]() {
      return std::make_unique<NonClientFrameViewWithFixedMargin>(margin());
    });
    params.native_widget = native_widget();
    widget()->Init(std::move(params));
    task_environment()->RunUntilIdle();
  }

  void TearDown() override {
    native_widget_.reset();
    widget()->Close();
    widget_.reset();
    ViewsTestBase::TearDown();
  }

  const gfx::Size& margin() const { return margin_; }
  Widget* widget() { return widget_.get(); }
  MockNativeWidget* native_widget() { return native_widget_.get(); }

 private:
  // Margin around the client view that should be excluded.
  const gfx::Size margin_;
  std::unique_ptr<Widget> widget_;
  std::unique_ptr<MockNativeWidget> native_widget_;

  // `NonClientFrameView` that pads the client view with a fixed-size margin,
  // to leave room for drawing that's not included in the aspect ratio.
  class NonClientFrameViewWithFixedMargin : public NonClientFrameView {
   public:
    // `margin` is the margin that we'll provide to our client view.
    explicit NonClientFrameViewWithFixedMargin(const gfx::Size& margin)
        : margin_(margin) {}

    // NonClientFrameView
    gfx::Rect GetBoundsForClientView() const override {
      gfx::Rect r = bounds();
      return gfx::Rect(r.x(), r.y(), r.width() - margin_.width(),
                       r.height() - margin_.height());
    }

    const gfx::Size margin_;
  };
};

TEST_P(WidgetSetAspectRatioTest, SetAspectRatioIncludesMargin) {
  // Provide a nonzero size.  It doesn't particularly matter what, as long as
  // it's larger than our margin.
  const gfx::Rect root_view_bounds(0, 0, 100, 200);
  ASSERT_GT(root_view_bounds.width(), margin().width());
  ASSERT_GT(root_view_bounds.height(), margin().height());
  widget()->non_client_view()->SetBoundsRect(root_view_bounds);

  // Verify that the excluded margin matches the margin that our custom
  // non-client frame provides.
  const gfx::SizeF aspect_ratio(1.5f, 1.0f);
  EXPECT_CALL(*native_widget(), SetAspectRatio(aspect_ratio, margin()));
  widget()->SetAspectRatio(aspect_ratio);
}

INSTANTIATE_TEST_SUITE_P(WidgetSetAspectRatioTestInstantiation,
                         WidgetSetAspectRatioTest,
                         ::testing::Values(gfx::Size(15, 20), gfx::Size(0, 0)));

class WidgetShadowTest : public WidgetTest {
 public:
  WidgetShadowTest() = default;

  WidgetShadowTest(const WidgetShadowTest&) = delete;
  WidgetShadowTest& operator=(const WidgetShadowTest&) = delete;

  ~WidgetShadowTest() override = default;

  // WidgetTest:
  void SetUp() override {
    set_native_widget_type(NativeWidgetType::kDesktop);
    WidgetTest::SetUp();
    InitControllers();
  }

  void TearDown() override {
#if defined(USE_AURA) && !BUILDFLAG(ENABLE_DESKTOP_AURA)
    shadow_controller_.reset();
    focus_controller_.reset();
#endif
    WidgetTest::TearDown();
  }

  Widget::InitParams CreateParams(Widget::InitParams::Type type) override {
    Widget::InitParams params =
        WidgetTest::CreateParams(override_type_.value_or(type));
    params.shadow_type = Widget::InitParams::ShadowType::kDrop;
    params.shadow_elevation = 10;
    params.name = name_;
    params.child = force_child_;
    return params;
  }

 protected:
  absl::optional<Widget::InitParams::Type> override_type_;
  std::string name_;
  bool force_child_ = false;

 private:
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
  void InitControllers() {}
#else
  class TestFocusRules : public wm::BaseFocusRules {
   public:
    TestFocusRules() = default;

    TestFocusRules(const TestFocusRules&) = delete;
    TestFocusRules& operator=(const TestFocusRules&) = delete;

    bool SupportsChildActivation(const aura::Window* window) const override {
      return true;
    }
  };

  void InitControllers() {
    focus_controller_ =
        std::make_unique<wm::FocusController>(new TestFocusRules);
    shadow_controller_ = std::make_unique<wm::ShadowController>(
        focus_controller_.get(), nullptr);
  }

  std::unique_ptr<wm::FocusController> focus_controller_;
  std::unique_ptr<wm::ShadowController> shadow_controller_;
#endif  // !BUILDFLAG(ENABLE_DESKTOP_AURA) && !BUILDFLAG(IS_MAC)
};

// Disabled on Mac: All drop shadows are managed out of process for now.
#if BUILDFLAG(IS_MAC)
#define MAYBE_ShadowsInRootWindow DISABLED_ShadowsInRootWindow
#else
#define MAYBE_ShadowsInRootWindow ShadowsInRootWindow
#endif

// Test that shadows are not added to root windows when created or upon
// activation. Test that shadows are added to non-root windows even if not
// activated.
TEST_F(WidgetShadowTest, MAYBE_ShadowsInRootWindow) {
#if defined(USE_AURA) && !BUILDFLAG(ENABLE_DESKTOP_AURA)
  // On ChromeOS, top-levels have shadows.
  bool top_level_window_should_have_shadow = true;
#else
  // On non-chromeos platforms, the hosting OS is responsible for the shadow.
  bool top_level_window_should_have_shadow = false;
#endif

  // To start, just create a Widget. This constructs the first ShadowController
  // which will start observing the environment for additional aura::Window
  // initialization. The very first ShadowController in DesktopNativeWidgetAura
  // is created after the call to aura::Window::Init(), so the ShadowController
  // Impl class won't ever see this first Window being initialized.
  name_ = "other_top_level";
  Widget* other_top_level = CreateTopLevelNativeWidget();

  name_ = "top_level";
  Widget* top_level = CreateTopLevelNativeWidget();
  top_level->SetBounds(gfx::Rect(100, 100, 320, 200));

  EXPECT_FALSE(WidgetHasInProcessShadow(top_level));
  EXPECT_FALSE(top_level->IsVisible());
  top_level->ShowInactive();
  EXPECT_EQ(top_level_window_should_have_shadow,
            WidgetHasInProcessShadow(top_level));
  top_level->Show();
  EXPECT_EQ(top_level_window_should_have_shadow,
            WidgetHasInProcessShadow(top_level));

  name_ = "control";
  Widget* control = CreateChildNativeWidgetWithParent(top_level);
  control->SetBounds(gfx::Rect(20, 20, 160, 100));

  // Widgets of TYPE_CONTROL become visible during Init, so start with a shadow.
  EXPECT_TRUE(WidgetHasInProcessShadow(control));
  control->ShowInactive();
  EXPECT_TRUE(WidgetHasInProcessShadow(control));
  control->Show();
  EXPECT_TRUE(WidgetHasInProcessShadow(control));

  name_ = "child";
  override_type_ = Widget::InitParams::TYPE_POPUP;
  force_child_ = true;
  Widget* child = CreateChildNativeWidgetWithParent(top_level);
  child->SetBounds(gfx::Rect(20, 20, 160, 100));

  // Now false: the Widget hasn't been shown yet.
  EXPECT_FALSE(WidgetHasInProcessShadow(child));
  child->ShowInactive();
  EXPECT_TRUE(WidgetHasInProcessShadow(child));
  child->Show();
  EXPECT_TRUE(WidgetHasInProcessShadow(child));

  other_top_level->Show();

  // Re-activate the top level window. This handles a hypothetical case where
  // a shadow is added via the ActivationChangeObserver rather than by the
  // aura::WindowObserver. Activation changes only modify an existing shadow
  // (if there is one), but should never install a Shadow, even if the Window
  // properties otherwise say it should have one.
  top_level->Show();
  EXPECT_EQ(top_level_window_should_have_shadow,
            WidgetHasInProcessShadow(top_level));

  top_level->Close();
  other_top_level->Close();
}

#if BUILDFLAG(IS_WIN)

// Tests the case where an intervening owner popup window is destroyed out from
// under the currently active modal top-level window. In this instance, the
// remaining top-level windows should be re-enabled.
TEST_F(DesktopWidgetTest, WindowModalOwnerDestroyedEnabledTest) {
  // top_level_widget owns owner_dialog_widget which owns owned_dialog_widget.
  std::unique_ptr<Widget> top_level_widget = CreateTestWidget();
  top_level_widget->Show();

  // Create the owner modal dialog.
  const auto create_params = [this](Widget* widget, gfx::NativeView parent) {
    Widget::InitParams init_params =
        CreateParamsForTestWidget(Widget::InitParams::TYPE_WINDOW);
    init_params.delegate = new WidgetDelegate();
    init_params.delegate->SetModalType(ui::MODAL_TYPE_WINDOW);
    init_params.parent = parent;
    init_params.native_widget =
        new test::TestPlatformNativeWidget<DesktopNativeWidgetAura>(
            widget, false, nullptr);
    return init_params;
  };
  Widget owner_dialog_widget;
  owner_dialog_widget.Init(
      create_params(&owner_dialog_widget, top_level_widget->GetNativeView()));
  owner_dialog_widget.Show();
  HWND owner_hwnd = HWNDForWidget(&owner_dialog_widget);

  // Create the owned modal dialog.
  Widget owned_dialog_widget;
  owned_dialog_widget.Init(
      create_params(&owned_dialog_widget, owner_dialog_widget.GetNativeView()));
  owned_dialog_widget.Show();
  HWND owned_hwnd = HWNDForWidget(&owned_dialog_widget);

  RunPendingMessages();

  HWND top_hwnd = HWNDForWidget(top_level_widget.get());

  EXPECT_FALSE(!!IsWindowEnabled(owner_hwnd));
  EXPECT_FALSE(!!IsWindowEnabled(top_hwnd));
  EXPECT_TRUE(!!IsWindowEnabled(owned_hwnd));

  owner_dialog_widget.CloseNow();
  RunPendingMessages();

  EXPECT_FALSE(!!IsWindow(owner_hwnd));
  EXPECT_FALSE(!!IsWindow(owned_hwnd));
  EXPECT_TRUE(!!IsWindowEnabled(top_hwnd));

  top_level_widget->CloseNow();
}

TEST_F(DesktopWidgetTest, StackAboveTest) {
  WidgetAutoclosePtr root_one(CreateTopLevelNativeWidget());
  WidgetAutoclosePtr root_two(CreateTopLevelNativeWidget());
  Widget* child_one = CreateChildNativeWidgetWithParent(root_one->AsWidget());
  Widget* child_one_b = CreateChildNativeWidgetWithParent(root_one->AsWidget());
  Widget* child_two = CreateChildNativeWidgetWithParent(root_two->AsWidget());
  Widget* grandchild_one =
      CreateChildNativeWidgetWithParent(child_one->AsWidget());
  Widget* grandchild_two =
      CreateChildNativeWidgetWithParent(child_two->AsWidget());

  root_one->ShowInactive();
  child_one->ShowInactive();
  child_one_b->ShowInactive();
  grandchild_one->ShowInactive();
  root_two->ShowInactive();
  child_two->ShowInactive();
  grandchild_two->ShowInactive();

  // Creates the following where Z-Order is from Left to Right.
  //            root_one                    root_two
  //             /    \                         /
  //       child_one_b  child_one           child_two
  //                       /                  /
  //                 grandchild_one    grandchild_two
  //
  // Note: child_one and grandchild_one were brought to front
  //       when grandchild_one was shown.

  // Child elements are stacked above parent.
  EXPECT_TRUE(child_one->IsStackedAbove(root_one->GetNativeView()));
  EXPECT_TRUE(child_one_b->IsStackedAbove(root_one->GetNativeView()));
  EXPECT_TRUE(grandchild_one->IsStackedAbove(child_one->GetNativeView()));
  EXPECT_TRUE(grandchild_two->IsStackedAbove(root_two->GetNativeView()));

  // Siblings with higher z-order are stacked correctly.
  EXPECT_TRUE(child_one->IsStackedAbove(child_one_b->GetNativeView()));
  EXPECT_TRUE(grandchild_one->IsStackedAbove(child_one_b->GetNativeView()));

  // Root elements are stacked above child of a root with lower z-order.
  EXPECT_TRUE(root_two->IsStackedAbove(root_one->GetNativeView()));
  EXPECT_TRUE(root_two->IsStackedAbove(child_one_b->GetNativeView()));

  // Child elements are stacked above child of root with lower z-order.
  EXPECT_TRUE(child_two->IsStackedAbove(child_one_b->GetNativeView()));
  EXPECT_TRUE(child_two->IsStackedAbove(grandchild_one->GetNativeView()));
  EXPECT_TRUE(grandchild_two->IsStackedAbove(child_one->GetNativeView()));
  EXPECT_TRUE(grandchild_two->IsStackedAbove(root_one->GetNativeView()));

  // False cases to verify function is not just returning true for all cases.
  EXPECT_FALSE(root_one->IsStackedAbove(grandchild_two->GetNativeView()));
  EXPECT_FALSE(root_one->IsStackedAbove(grandchild_one->GetNativeView()));
  EXPECT_FALSE(child_two->IsStackedAbove(grandchild_two->GetNativeView()));
  EXPECT_FALSE(child_one->IsStackedAbove(grandchild_two->GetNativeView()));
  EXPECT_FALSE(child_one_b->IsStackedAbove(child_two->GetNativeView()));
  EXPECT_FALSE(grandchild_one->IsStackedAbove(grandchild_two->GetNativeView()));
  EXPECT_FALSE(grandchild_one->IsStackedAbove(root_two->GetNativeView()));
  EXPECT_FALSE(child_one_b->IsStackedAbove(grandchild_one->GetNativeView()));
}

#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)

namespace {

bool CanHaveCompositingManager() {
#if BUILDFLAG(IS_OZONE)
  auto* const egl_utility =
      ui::OzonePlatform::GetInstance()->GetPlatformGLEGLUtility();
  return (egl_utility != nullptr) && egl_utility->HasVisualManager();
#else
  return false;
#endif
}

bool ExpectWidgetTransparency(Widget::InitParams::WindowOpacity opacity) {
  switch (opacity) {
    case Widget::InitParams::WindowOpacity::kOpaque:
      return false;
    case Widget::InitParams::WindowOpacity::kTranslucent:
      return true;
    case Widget::InitParams::WindowOpacity::kInferred:
      ADD_FAILURE() << "WidgetOpacity must be explicitly set";
      return false;
  }
}

class CompositingWidgetTest : public DesktopWidgetTest {
 public:
  CompositingWidgetTest()
      : widget_types_{Widget::InitParams::TYPE_WINDOW,
                      Widget::InitParams::TYPE_WINDOW_FRAMELESS,
                      Widget::InitParams::TYPE_CONTROL,
                      Widget::InitParams::TYPE_POPUP,
                      Widget::InitParams::TYPE_MENU,
                      Widget::InitParams::TYPE_TOOLTIP,
                      Widget::InitParams::TYPE_BUBBLE,
                      Widget::InitParams::TYPE_DRAG} {}

  CompositingWidgetTest(const CompositingWidgetTest&) = delete;
  CompositingWidgetTest& operator=(const CompositingWidgetTest&) = delete;

  ~CompositingWidgetTest() override = default;

  Widget::InitParams CreateParams(Widget::InitParams::Type type) override {
    Widget::InitParams params = DesktopWidgetTest::CreateParams(type);
    params.opacity = opacity_;
    return params;
  }

  void CheckAllWidgetsForOpacity(
      const Widget::InitParams::WindowOpacity opacity) {
    opacity_ = opacity;
    for (const auto& widget_type : widget_types_) {
#if BUILDFLAG(IS_MAC)
      // Tooltips are native on Mac. See NativeWidgetNSWindowBridge::Init.
      if (widget_type == Widget::InitParams::TYPE_TOOLTIP)
        continue;
#elif BUILDFLAG(IS_WIN)
      // Other widget types would require to create a parent window and the
      // the purpose of this test is mainly X11 in the first place.
      if (widget_type != Widget::InitParams::TYPE_WINDOW)
        continue;
#endif
      std::unique_ptr<Widget> widget = CreateTestWidget(widget_type);

      // Use NativeWidgetAura directly.
      if (widget_type == Widget::InitParams::TYPE_WINDOW_FRAMELESS ||
          widget_type == Widget::InitParams::TYPE_CONTROL)
        continue;

#if BUILDFLAG(IS_MAC)
      // Mac always always has a compositing window manager, but doesn't have
      // transparent titlebars which is what ShouldWindowContentsBeTransparent()
      // is currently used for. Asking for transparency should get it. Note that
      // TestViewsDelegate::use_transparent_windows_ determines the result of
      // kInferOpacity: assume it is false.
      bool should_be_transparent =
          opacity_ == Widget::InitParams::WindowOpacity::kTranslucent;
#else
      bool should_be_transparent = widget->ShouldWindowContentsBeTransparent();
#endif

      EXPECT_EQ(IsNativeWindowTransparent(widget->GetNativeWindow()),
                should_be_transparent);

      if (CanHaveCompositingManager()) {
        if (HasCompositingManager() && ExpectWidgetTransparency(opacity))
          EXPECT_TRUE(widget->IsTranslucentWindowOpacitySupported());
        else
          EXPECT_FALSE(widget->IsTranslucentWindowOpacitySupported());
      }
    }
  }

 protected:
  const std::vector<Widget::InitParams::Type> widget_types_;
  Widget::InitParams::WindowOpacity opacity_ =
      Widget::InitParams::WindowOpacity::kInferred;
};

}  // namespace

// Only test manually set opacity via kOpaque or kTranslucent.  kInferred is
// unpredictable and depends on the platform and window type.
TEST_F(CompositingWidgetTest, Transparency_DesktopWidgetOpaque) {
  CheckAllWidgetsForOpacity(Widget::InitParams::WindowOpacity::kOpaque);
}

TEST_F(CompositingWidgetTest, Transparency_DesktopWidgetTranslucent) {
  CheckAllWidgetsForOpacity(Widget::InitParams::WindowOpacity::kTranslucent);
}

#endif  // BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)

}  // namespace views::test