chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc

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

#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/window_parenting_client.h"
#include "ui/aura/env.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/window_occlusion_tracker_test_api.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/display/screen.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view_constants_aura.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include "ui/base/view_prop.h"
#include "ui/base/win/window_event_target.h"
#include "ui/views/win/hwnd_util.h"
#endif

namespace views::test {

DesktopNativeWidgetAuraTest;

// Verifies creating a Widget with a parent that is not in a RootWindow doesn't
// crash.
TEST_F(DesktopNativeWidgetAuraTest, CreateWithParentNotInRootWindow) {}

// Verifies that the Aura windows making up a widget instance have the correct
// bounds after the widget is resized.
TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowSizeTest) {}

// Verifies GetNativeView() is initially hidden. If the native view is initially
// shown then animations can not be disabled.
TEST_F(DesktopNativeWidgetAuraTest, NativeViewInitiallyHidden) {}

// Verifies that the native view isn't activated if Widget requires that.
TEST_F(DesktopNativeWidgetAuraTest, NativeViewNoActivate) {}

#if BUILDFLAG(IS_WIN)
// Verifies that if the DesktopWindowTreeHost is already shown, the native view
// still reports not visible as we haven't shown the content window.
TEST_F(DesktopNativeWidgetAuraTest, WidgetNotVisibleOnlyWindowTreeHostShown) {
  Widget widget;
  Widget::InitParams init_params = CreateParams(
      Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
  widget.Init(std::move(init_params));
  ShowWindow(widget.GetNativeView()->GetHost()->GetAcceleratedWidget(),
             SW_SHOWNORMAL);
  EXPECT_FALSE(widget.IsVisible());
}
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
// TODO(crbug.com/40607034): investigate fixing and enabling on Chrome OS.
#define MAYBE_GlobalCursorState
#else
#define MAYBE_GlobalCursorState
#endif

// Verify that the cursor state is shared between two native widgets.
TEST_F(DesktopNativeWidgetAuraTest, MAYBE_GlobalCursorState) {}

// Verifies FocusController doesn't attempt to access |content_window_| during
// destruction. Previously the FocusController was destroyed after the window.
// This could be problematic as FocusController references |content_window_| and
// could attempt to use it after |content_window_| was destroyed. This test
// verifies this doesn't happen. Note that this test only failed under ASAN.
TEST_F(DesktopNativeWidgetAuraTest, DontAccessContentWindowDuringDestruction) {}

namespace {

std::unique_ptr<Widget> CreateAndShowControlWidget(aura::Window* parent) {}

}  // namespace

#if BUILDFLAG(IS_CHROMEOS_ASH)
// TODO(crbug.com/40607034): investigate fixing and enabling on Chrome OS.
#define MAYBE_ReorderDoesntRecomputeOcclusion
#else
#define MAYBE_ReorderDoesntRecomputeOcclusion
#endif

TEST_F(DesktopNativeWidgetAuraTest, MAYBE_ReorderDoesntRecomputeOcclusion) {}

void QuitNestedLoopAndCloseWidget(std::unique_ptr<Widget> widget,
                                  base::OnceClosure quit_runloop) {}

// Verifies that a widget can be destroyed when running a nested message-loop.
TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) {}

// DesktopNativeWidgetAura::CloseNow is protected.
// Create a new object to override CloseNow so we can test deleting
// the native widget.
class TestDesktopNativeWidgetAura : public DesktopNativeWidgetAura {};

// Series of tests that verifies a null NativeWidgetDelegate does not cause
// a crash.
class DesktopNativeWidgetAuraWithNoDelegateTest
    : public DesktopNativeWidgetAuraTest {};

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, GetHitTestMaskTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, GetMaximumSizeTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, GetMinimumSizeTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, GetNonClientComponentTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, GetWidgetTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, HasHitTestMaskTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnCaptureLostTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnGestureEventTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnHostMovedInPixelsTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnHostResizedTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnHostWorkspaceChangedTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnKeyEventTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnMouseEventTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnPaintTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnScrollEventTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnWindowActivatedTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, OnWindowFocusedTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, ShouldActivateTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest,
       ShouldDescendIntoChildForEventHandlingTest) {}

TEST_F(DesktopNativeWidgetAuraWithNoDelegateTest, UpdateVisualStateTest) {}

#if !BUILDFLAG(IS_FUCHSIA)
// TODO(crbug.com/40192931): Under Fuchsia pop-up and fullscreen windows are not
// reparented to be top-level, so the following tests are not valid.

// This class provides functionality to create fullscreen and top level popup
// windows. It additionally tests whether the destruction of these windows
// occurs correctly in desktop AURA without crashing.
// It provides facilities to test the following cases:-
// 1. Child window destroyed which should lead to the destruction of the
//    parent.
// 2. Parent window destroyed which should lead to the child being destroyed.
class DesktopAuraTopLevelWindowTest : public aura::WindowObserver {};

TEST_F(DesktopNativeWidgetAuraTest, FullscreenWindowDestroyedBeforeOwnerTest) {}

TEST_F(DesktopNativeWidgetAuraTest, FullscreenWindowOwnerDestroyed) {}

TEST_F(DesktopNativeWidgetAuraTest, TopLevelOwnedPopupTest) {}

// This test validates that when a top level owned popup Aura window is
// resized, the widget is resized as well.
TEST_F(DesktopNativeWidgetAuraTest, TopLevelOwnedPopupResizeTest) {}

// This test validates that when a top level owned popup Aura window is
// repositioned, the widget is repositioned as well.
TEST_F(DesktopNativeWidgetAuraTest, TopLevelOwnedPopupRepositionTest) {}

#endif  // !BUILDFLAG(IS_FUCHSIA)

// The following code verifies we can correctly destroy a Widget from a mouse
// enter/exit. We could test move/drag/enter/exit but in general we don't run
// nested run loops from such events, nor has the code ever really dealt
// with this situation.

// Generates two moves (first generates enter, second real move), a press, drag
// and release stopping at |last_event_type|.
void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) {}

// Creates a widget and invokes GenerateMouseEvents() with |last_event_type|.
void RunCloseWidgetDuringDispatchTest(WidgetTest* test,
                                      ui::EventType last_event_type) {}

// Verifies deleting the widget from a mouse pressed event doesn't crash.
TEST_F(DesktopNativeWidgetAuraTest, CloseWidgetDuringMousePress) {}

// Verifies deleting the widget from a mouse released event doesn't crash.
TEST_F(DesktopNativeWidgetAuraTest, CloseWidgetDuringMouseReleased) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// TODO(crbug.com/40607034): investigate fixing and enabling on Chrome OS.
#define MAYBE_WindowMouseModalityTest
#else
#define MAYBE_WindowMouseModalityTest
#endif

// This test verifies that whether mouse events when a modal dialog is
// displayed are eaten or received by the dialog.
TEST_F(DesktopNativeWidgetAuraTest, MAYBE_WindowMouseModalityTest) {}

#if BUILDFLAG(IS_WIN)
// Tests whether we can activate the top level widget when a modal dialog is
// active.
TEST_F(DesktopNativeWidgetAuraTest, WindowModalityActivationTest) {
  TestDesktopWidgetDelegate widget_delegate;
  widget_delegate.InitWidget(
      CreateParams(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
                   Widget::InitParams::TYPE_WINDOW));

  Widget* top_level_widget = widget_delegate.GetWidget();
  top_level_widget->Show();
  EXPECT_TRUE(top_level_widget->IsVisible());

  HWND win32_window = views::HWNDForWidget(top_level_widget);
  EXPECT_TRUE(::IsWindow(win32_window));

  // We should be able to activate the window even if the WidgetDelegate
  // says no, when a modal dialog is active.
  widget_delegate.SetCanActivate(false);

  auto dialog_delegate = std::make_unique<DialogDelegateView>();
  dialog_delegate->SetModalType(ui::mojom::ModalType::kWindow);

  Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
      dialog_delegate.release(), nullptr, top_level_widget->GetNativeView());
  modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
  modal_dialog_widget->Show();
  EXPECT_TRUE(modal_dialog_widget->IsVisible());

  LRESULT activate_result = ::SendMessage(
      win32_window, WM_MOUSEACTIVATE, reinterpret_cast<WPARAM>(win32_window),
      MAKELPARAM(WM_LBUTTONDOWN, HTCLIENT));
  EXPECT_EQ(activate_result, MA_ACTIVATE);

  modal_dialog_widget->CloseNow();
}

// This test validates that sending WM_CHAR/WM_SYSCHAR/WM_SYSDEADCHAR
// messages via the WindowEventTarget interface implemented by the
// HWNDMessageHandler class does not cause a crash due to an unprocessed
// event
TEST_F(DesktopNativeWidgetAuraTest,
       CharMessagesAsKeyboardMessagesDoesNotCrash) {
  Widget widget;
  Widget::InitParams params = CreateParams(
      Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
  widget.Init(std::move(params));
  widget.Show();

  ui::WindowEventTarget* target =
      reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
          widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
          ui::WindowEventTarget::kWin32InputEventTarget));
  ASSERT_NE(nullptr, target);
  bool handled = false;
  target->HandleKeyboardMessage(WM_CHAR, 0, 0, &handled);
  target->HandleKeyboardMessage(WM_SYSCHAR, 0, 0, &handled);
  target->HandleKeyboardMessage(WM_SYSDEADCHAR, 0, 0, &handled);
  widget.CloseNow();
}

#endif  // BUILDFLAG(IS_WIN)

// Tests that reparenting a destkop widget to another desktop widget does not
// crash.
TEST_F(DesktopNativeWidgetAuraTest, Reparent) {}

}  // namespace views::test