#include <stddef.h>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/base/mojom/window_show_state.mojom.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/test/ui_controls.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/native_ui_types.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/test/focus_manager_test.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/widget_activation_waiter.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/touchui/touch_selection_controller_impl.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_interactive_uitest_utils.h"
#include "ui/views/widget/widget_utils.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/wm/public/activation_client.h"
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/aura/env.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "ui/aura/input_state_lookup.h"
#include "ui/aura/test/env_test_helper.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/views/win/hwnd_util.h"
#endif
namespace views::test {
namespace {
class ExitLoopOnRelease : public View {
METADATA_HEADER(ExitLoopOnRelease, View)
public:
explicit ExitLoopOnRelease(base::OnceClosure quit_closure)
: quit_closure_(std::move(quit_closure)) {
DCHECK(quit_closure_);
}
ExitLoopOnRelease(const ExitLoopOnRelease&) = delete;
ExitLoopOnRelease& operator=(const ExitLoopOnRelease&) = delete;
~ExitLoopOnRelease() override = default;
private:
void OnMouseReleased(const ui::MouseEvent& event) override {
GetWidget()->Close();
std::move(quit_closure_).Run();
}
base::OnceClosure quit_closure_;
};
BEGIN_METADATA(ExitLoopOnRelease)
END_METADATA
class GestureCaptureView : public View {
METADATA_HEADER(GestureCaptureView, View)
public:
GestureCaptureView() = default;
GestureCaptureView(const GestureCaptureView&) = delete;
GestureCaptureView& operator=(const GestureCaptureView&) = delete;
~GestureCaptureView() override = default;
private:
void OnGestureEvent(ui::GestureEvent* event) override {
if (event->type() == ui::EventType::kGestureTapDown) {
GetWidget()->SetCapture(this);
event->StopPropagation();
}
}
};
BEGIN_METADATA(GestureCaptureView)
END_METADATA
class MouseView : public View {
METADATA_HEADER(MouseView, View)
public:
MouseView() = default;
MouseView(const MouseView&) = delete;
MouseView& operator=(const MouseView&) = delete;
~MouseView() override = default;
bool OnMousePressed(const ui::MouseEvent& event) override {
pressed_++;
return true;
}
void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }
int EnteredCalls() {
int i = entered_;
entered_ = 0;
return i;
}
int ExitedCalls() {
int i = exited_;
exited_ = 0;
return i;
}
int pressed() const { return pressed_; }
private:
int entered_ = 0;
int exited_ = 0;
int pressed_ = 0;
};
BEGIN_METADATA(MouseView)
END_METADATA
class NestedLoopCaptureView : public View {
METADATA_HEADER(NestedLoopCaptureView, View)
public:
explicit NestedLoopCaptureView(std::unique_ptr<Widget> widget)
: run_loop_(base::RunLoop::Type::kNestableTasksAllowed),
widget_(std::move(widget)) {}
NestedLoopCaptureView(const NestedLoopCaptureView&) = delete;
NestedLoopCaptureView& operator=(const NestedLoopCaptureView&) = delete;
~NestedLoopCaptureView() override = default;
base::OnceClosure GetQuitClosure() { return run_loop_.QuitClosure(); }
private:
bool OnMousePressed(const ui::MouseEvent& event) override {
widget_->Show();
widget_->SetCapture(widget_->GetContentsView());
EXPECT_TRUE(widget_->HasCapture());
run_loop_.Run();
return true;
}
base::RunLoop run_loop_;
std::unique_ptr<Widget> widget_;
};
BEGIN_METADATA(NestedLoopCaptureView)
END_METADATA
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
class DragView : public View, public DragController {
METADATA_HEADER(DragView, View)
public:
DragView(base::OnceClosure on_drag_enter,
base::OnceClosure on_drag_exit,
base::OnceClosure on_capture_lost,
base::OnceClosure on_mouse_exit)
: on_drag_enter_(std::move(on_drag_enter)),
on_drag_exit_(std::move(on_drag_exit)),
on_capture_lost_(std::move(on_capture_lost)),
on_mouse_exit_(std::move(on_mouse_exit)) {
set_drag_controller(this);
}
DragView(const DragView&) = delete;
DragView& operator=(const DragView&) = delete;
~DragView() override = default;
private:
bool CanStartDragForView(views::View* sender,
const gfx::Point& press_pt,
const gfx::Point& current_pt) override {
EXPECT_EQ(sender, this);
return true;
}
int GetDragOperationsForView(views::View* sender,
const gfx::Point& press_pt) override {
EXPECT_EQ(sender, this);
return ui::DragDropTypes::DRAG_COPY;
}
void WriteDragDataForView(views::View* sender,
const gfx::Point& press_pt,
ui::OSExchangeData* data) override {
data->provider().SetString(u"test");
}
bool GetDropFormats(
int* formats,
std::set<ui::ClipboardFormatType>* format_types) override {
*formats = ui::OSExchangeData::STRING;
return true;
}
bool CanDrop(const OSExchangeData& data) override { return true; }
void OnDragEntered(const ui::DropTargetEvent& event) override {
if (on_drag_enter_) {
std::move(on_drag_enter_).Run();
}
}
void OnDragExited() override {
if (on_drag_exit_) {
std::move(on_drag_exit_).Run();
}
}
void OnMouseCaptureLost() override {
if (on_capture_lost_) {
std::move(on_capture_lost_).Run();
}
}
void OnMouseExited(const ui::MouseEvent& event) override {
if (on_mouse_exit_) {
std::move(on_mouse_exit_).Run();
}
}
base::OnceClosure on_drag_enter_, on_drag_exit_, on_capture_lost_,
on_mouse_exit_;
};
BEGIN_METADATA(DragView)
END_METADATA
#endif
class MouseEventRootView : public internal::RootView {
METADATA_HEADER(MouseEventRootView, View)
public:
using RootView::RootView;
MouseEventRootView(const MouseEventRootView&) = delete;
MouseEventRootView& operator=(const MouseEventRootView&) = delete;
~MouseEventRootView() override = default;
bool OnMouseDragged(const ui::MouseEvent& event) override {
++dragged_;
return internal::RootView::OnMouseDragged(event);
}
void OnMouseMoved(const ui::MouseEvent& event) override {
++moved_;
internal::RootView::OnMouseMoved(event);
}
int moved() const { return moved_; }
int dragged() const { return dragged_; }
void reset_counts() {
moved_ = 0;
dragged_ = 0;
}
private:
int dragged_ = 0;
int moved_ = 0;
};
BEGIN_METADATA(MouseEventRootView)
END_METADATA
class MouseEventWidget : public Widget {
METADATA_HEADER(MouseEventWidget, Widget)
public:
MouseEventWidget() = default;
MouseEventWidget(const MouseEventWidget&) = delete;
MouseEventWidget& operator=(const MouseEventWidget&) = delete;
~MouseEventWidget() override = default;
MouseEventRootView* root_view() { return root_view_; }
private:
internal::RootView* CreateRootView() override {
root_view_ = new MouseEventRootView(this);
return root_view_;
}
raw_ptr<MouseEventRootView> root_view_;
};
BEGIN_METADATA(MouseEventWidget)
END_METADATA
ui::mojom::WindowShowState GetWidgetShowState(const Widget* widget) {
if (widget->IsFullscreen()) {
return ui::mojom::WindowShowState::kFullscreen;
}
if (widget->IsMaximized()) {
return ui::mojom::WindowShowState::kMaximized;
}
if (widget->IsMinimized()) {
return ui::mojom::WindowShowState::kMinimized;
}
return widget->IsActive() ? ui::mojom::WindowShowState::kNormal
: ui::mojom::WindowShowState::kInactive;
}
void RunPendingMessagesForActiveStatusChange() {
#if BUILDFLAG(IS_MAC)
base::RunLoop().RunUntilIdle();
#endif
}
void ActivateSync(Widget* widget) {
widget->Activate();
views::test::WaitForWidgetActive(widget, true);
}
void ShowSync(Widget* widget) {
widget->Show();
views::test::WaitForWidgetActive(widget, true);
}
void DeactivateSync(Widget* widget) {
#if BUILDFLAG(IS_MAC)
widget->widget_delegate()->SetCanActivate(false);
Widget* stealer = new Widget;
stealer->Init(
Widget::InitParams(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW));
ShowSync(stealer);
stealer->CloseNow();
widget->widget_delegate()->SetCanActivate(true);
#else
widget->Deactivate();
views::test::WaitForWidgetActive(widget, false);
#endif
}
#if BUILDFLAG(IS_WIN)
void ActivatePlatformWindow(Widget* widget) {
::SetActiveWindow(
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
}
#endif
void ShowInactiveSync(Widget* widget) {
widget->ShowInactive();
RunPendingMessagesForActiveStatusChange();
}
std::unique_ptr<Textfield> CreateTextfield() {
auto textfield = std::make_unique<Textfield>();
textfield->GetViewAccessibility().SetName(u"Foo");
return textfield;
}
}
class WidgetTestInteractive : public WidgetTest {
public:
WidgetTestInteractive() = default;
~WidgetTestInteractive() override = default;
void SetUp() override {
SetUpForInteractiveTests();
WidgetTest::SetUp();
}
};
#if BUILDFLAG(IS_WIN)
TEST_F(DesktopWidgetTestInteractive,
DesktopNativeWidgetAuraActivationAndFocusTest) {
View* focusable_view1 = new View;
focusable_view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
std::unique_ptr<Widget> widget1 = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
widget1->GetContentsView()->AddChildViewRaw(focusable_view1);
widget1->Show();
aura::Window* root_window1 = GetRootWindow(widget1.get());
focusable_view1->RequestFocus();
EXPECT_TRUE(root_window1 != nullptr);
wm::ActivationClient* activation_client1 =
wm::GetActivationClient(root_window1);
EXPECT_TRUE(activation_client1 != nullptr);
EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
View* focusable_view2 = new View;
std::unique_ptr<Widget> widget2 = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
widget1->GetContentsView()->AddChildViewRaw(focusable_view2);
widget2->Show();
aura::Window* root_window2 = GetRootWindow(widget2.get());
focusable_view2->RequestFocus();
ActivatePlatformWindow(widget2.get());
wm::ActivationClient* activation_client2 =
wm::GetActivationClient(root_window2);
EXPECT_TRUE(activation_client2 != nullptr);
EXPECT_EQ(activation_client2->GetActiveWindow(), widget2->GetNativeView());
EXPECT_EQ(activation_client1->GetActiveWindow(),
reinterpret_cast<aura::Window*>(NULL));
focusable_view1->RequestFocus();
ActivatePlatformWindow(widget1.get());
EXPECT_EQ(activation_client2->GetActiveWindow(),
reinterpret_cast<aura::Window*>(NULL));
EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
}
TEST_F(DesktopWidgetTestInteractive, FocusChangesOnBubble) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
View* focusable_view =
widget->GetContentsView()->AddChildView(std::make_unique<View>());
focusable_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget->Show();
focusable_view->RequestFocus();
EXPECT_TRUE(focusable_view->HasFocus());
auto owned_bubble_delegate_view =
std::make_unique<views::BubbleDialogDelegateView>(
BubbleDialogDelegateView::CreatePassKey(), focusable_view,
BubbleBorder::NONE);
owned_bubble_delegate_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
BubbleDialogDelegateView* bubble_delegate_view =
owned_bubble_delegate_view.get();
BubbleDialogDelegateView::CreateBubble(std::move(owned_bubble_delegate_view))
->Show();
bubble_delegate_view->RequestFocus();
EXPECT_FALSE(focusable_view->HasFocus());
EXPECT_TRUE(bubble_delegate_view->HasFocus());
bubble_delegate_view->GetWidget()->CloseNow();
EXPECT_TRUE(focusable_view->HasFocus());
}
class TouchEventHandler : public ui::EventHandler {
public:
explicit TouchEventHandler(Widget* widget) : widget_(widget) {
widget_->GetNativeWindow()->GetHost()->window()->AddPreTargetHandler(this);
}
TouchEventHandler(const TouchEventHandler&) = delete;
TouchEventHandler& operator=(const TouchEventHandler&) = delete;
~TouchEventHandler() override {
widget_->GetNativeWindow()->GetHost()->window()->RemovePreTargetHandler(
this);
}
void WaitForEvents() {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
static void __stdcall AsyncActivateMouse(HWND hwnd,
UINT msg,
ULONG_PTR data,
LRESULT result) {
EXPECT_EQ(MA_NOACTIVATE, result);
std::move(reinterpret_cast<TouchEventHandler*>(data)->quit_closure_).Run();
}
void ActivateViaMouse() {
SendMessageCallback(
widget_->GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
WM_MOUSEACTIVATE, 0, 0, AsyncActivateMouse,
reinterpret_cast<ULONG_PTR>(this));
}
private:
void OnTouchEvent(ui::TouchEvent* event) override {
if (event->type() == ui::EventType::kTouchPressed) {
ActivateViaMouse();
}
}
raw_ptr<Widget> widget_;
base::OnceClosure quit_closure_;
};
TEST_F(DesktopWidgetTestInteractive, DISABLED_TouchNoActivateWindow) {
View* focusable_view = new View;
focusable_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
widget->GetContentsView()->AddChildViewRaw(focusable_view);
widget->Show();
{
TouchEventHandler touch_event_handler(widget.get());
ASSERT_TRUE(
ui_controls::SendTouchEvents(ui_controls::kTouchPress, 1, 100, 100));
touch_event_handler.WaitForEvents();
}
}
#endif
TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
std::unique_ptr<Widget> toplevel =
base::WrapUnique(CreateTopLevelFramelessPlatformWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
MouseView* view = new MouseView();
view->SetBounds(90, 90, 10, 10);
toplevel->GetRootView()->SetLayoutManager(nullptr);
toplevel->GetRootView()->AddChildViewRaw(view);
toplevel->Show();
RunPendingMessages();
gfx::Point p1(200, 200);
ui::MouseEvent moved_out(ui::EventType::kMouseMoved, p1, p1,
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
toplevel->OnMouseEvent(&moved_out);
EXPECT_EQ(0, view->EnteredCalls());
EXPECT_EQ(0, view->ExitedCalls());
gfx::Point p2(95, 95);
ui::MouseEvent moved_over(ui::EventType::kMouseMoved, p2, p2,
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
toplevel->OnMouseEvent(&moved_over);
EXPECT_EQ(1, view->EnteredCalls());
EXPECT_EQ(0, view->ExitedCalls());
gfx::Point p3(102, 95);
ui::MouseEvent moved_resizer(ui::EventType::kMouseMoved, p3, p3,
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
toplevel->OnMouseEvent(&moved_resizer);
EXPECT_EQ(0, view->EnteredCalls());
EXPECT_EQ(1, view->ExitedCalls());
toplevel->OnMouseEvent(&moved_over);
EXPECT_EQ(1, view->EnteredCalls());
EXPECT_EQ(0, view->ExitedCalls());
}
TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
std::unique_ptr<Widget> widget1 = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
View* view1 =
widget1->GetContentsView()->AddChildView(std::make_unique<View>());
view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
std::unique_ptr<Widget> widget2 = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
View* view2a = new View;
View* view2b = new View;
view2a->SetFocusBehavior(View::FocusBehavior::ALWAYS);
view2b->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget2->GetContentsView()->AddChildViewRaw(view2a);
widget2->GetContentsView()->AddChildViewRaw(view2b);
ShowSync(widget1.get());
EXPECT_TRUE(widget1->IsActive());
view1->RequestFocus();
EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
ShowSync(widget2.get());
EXPECT_TRUE(widget2->IsActive());
EXPECT_FALSE(widget1->IsActive());
EXPECT_EQ(nullptr, widget1->GetFocusManager()->GetFocusedView());
view2a->RequestFocus();
EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
view2b->RequestFocus();
EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
ActivateSync(widget1.get());
EXPECT_TRUE(widget1->IsActive());
EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
EXPECT_FALSE(widget2->IsActive());
EXPECT_EQ(nullptr, widget2->GetFocusManager()->GetFocusedView());
ActivateSync(widget2.get());
EXPECT_TRUE(widget2->IsActive());
EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
EXPECT_FALSE(widget1->IsActive());
EXPECT_EQ(nullptr, widget1->GetFocusManager()->GetFocusedView());
}
TEST_F(WidgetTestInteractive, ZOrderCheckBetweenTopWindows) {
std::unique_ptr<Widget> w1 = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
std::unique_ptr<Widget> w2 = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
std::unique_ptr<Widget> w3 = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(w1.get());
ShowSync(w2.get());
ShowSync(w3.get());
EXPECT_FALSE(w1->AsWidget()->IsStackedAbove(w2->AsWidget()->GetNativeView()));
EXPECT_FALSE(w2->AsWidget()->IsStackedAbove(w3->AsWidget()->GetNativeView()));
EXPECT_FALSE(w1->AsWidget()->IsStackedAbove(w3->AsWidget()->GetNativeView()));
EXPECT_TRUE(w2->AsWidget()->IsStackedAbove(w1->AsWidget()->GetNativeView()));
EXPECT_TRUE(w3->AsWidget()->IsStackedAbove(w2->AsWidget()->GetNativeView()));
EXPECT_TRUE(w3->AsWidget()->IsStackedAbove(w1->AsWidget()->GetNativeView()));
w2->AsWidget()->StackAboveWidget(w1->AsWidget());
EXPECT_TRUE(w2->AsWidget()->IsStackedAbove(w1->AsWidget()->GetNativeView()));
w1->AsWidget()->StackAboveWidget(w2->AsWidget());
EXPECT_FALSE(w2->AsWidget()->IsStackedAbove(w1->AsWidget()->GetNativeView()));
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_ChildStackedRelativeToParent DISABLED_ChildStackedRelativeToParent
#else
#define MAYBE_ChildStackedRelativeToParent ChildStackedRelativeToParent
#endif
TEST_F(WidgetTestInteractive, MAYBE_ChildStackedRelativeToParent) {
WidgetAutoclosePtr parent(CreateTopLevelPlatformWidget());
Widget* child = CreateChildPlatformWidget(parent->GetNativeView());
parent->SetBounds(gfx::Rect(160, 100, 320, 200));
child->SetBounds(gfx::Rect(50, 50, 30, 20));
child->ShowInactive();
EXPECT_FALSE(child->IsVisible());
ShowSync(parent.get());
EXPECT_TRUE(child->IsVisible());
EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
EXPECT_FALSE(IsWindowStackedAbove(parent.get(), child));
WidgetAutoclosePtr popover(CreateTopLevelPlatformWidget());
popover->SetBounds(gfx::Rect(150, 90, 340, 240));
ShowSync(popover.get());
EXPECT_TRUE(IsWindowStackedAbove(popover.get(), child));
EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
ShowSync(parent.get());
EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
Widget* grandchild = CreateChildPlatformWidget(child->GetNativeView());
grandchild->SetBounds(gfx::Rect(5, 5, 15, 10));
grandchild->ShowInactive();
EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
ShowSync(popover.get());
EXPECT_TRUE(IsWindowStackedAbove(popover.get(), grandchild));
EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
ShowSync(parent.get());
EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
EXPECT_TRUE(IsWindowStackedAbove(child, popover.get()));
parent->Hide();
EXPECT_FALSE(grandchild->IsVisible());
ShowSync(parent.get());
EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
grandchild->Hide();
EXPECT_FALSE(grandchild->IsVisible());
grandchild->ShowInactive();
EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
}
TEST_F(WidgetTestInteractive, ChildWidgetStackAbove) {
WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
auto children = std::to_array<Widget*>(
{CreateChildPlatformWidget(toplevel->GetNativeView()),
CreateChildPlatformWidget(toplevel->GetNativeView()),
CreateChildPlatformWidget(toplevel->GetNativeView())});
auto order = std::to_array<size_t>({0, 1, 2});
static_assert(children.size() == order.size());
children[0]->ShowInactive();
children[1]->ShowInactive();
children[2]->ShowInactive();
ShowSync(toplevel.get());
do {
children[order[1]]->StackAboveWidget(children[order[0]]);
children[order[2]]->StackAboveWidget(children[order[1]]);
for (size_t i = 0; i < order.size(); i++) {
for (size_t j = 0; j < order.size(); j++) {
if (i < j) {
EXPECT_FALSE(
IsWindowStackedAbove(children[order[i]], children[order[j]]));
} else if (i > j) {
EXPECT_TRUE(
IsWindowStackedAbove(children[order[i]], children[order[j]]));
}
}
}
} while (std::next_permutation(order.begin(), order.end()));
}
TEST_F(WidgetTestInteractive, ChildWidgetStackAtTop) {
WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
auto children = std::to_array<Widget*>(
{CreateChildPlatformWidget(toplevel->GetNativeView()),
CreateChildPlatformWidget(toplevel->GetNativeView()),
CreateChildPlatformWidget(toplevel->GetNativeView())});
auto order = std::to_array<size_t>({0, 1, 2});
static_assert(children.size() == order.size());
children[0]->ShowInactive();
children[1]->ShowInactive();
children[2]->ShowInactive();
ShowSync(toplevel.get());
do {
children[order[1]]->StackAtTop();
children[order[2]]->StackAtTop();
for (size_t i = 0; i < order.size(); i++) {
for (size_t j = 0; j < order.size(); j++) {
if (i < j) {
EXPECT_FALSE(
IsWindowStackedAbove(children[order[i]], children[order[j]]));
} else if (i > j) {
EXPECT_TRUE(
IsWindowStackedAbove(children[order[i]], children[order[j]]));
}
}
}
} while (std::next_permutation(order.begin(), order.end()));
}
#if BUILDFLAG(IS_WIN)
TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget());
widget->SetContentsView(std::make_unique<View>());
for (size_t i = 0; i < 2; ++i) {
auto child = std::make_unique<View>();
child->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget->GetContentsView()->AddChildView(std::move(child));
}
widget->Show();
widget->GetNativeWindow()->GetHost()->Show();
const HWND hwnd = HWNDForWidget(widget.get());
EXPECT_TRUE(::IsWindow(hwnd));
EXPECT_TRUE(::IsWindowEnabled(hwnd));
EXPECT_EQ(hwnd, ::GetActiveWindow());
for (View* view : widget->GetContentsView()->children()) {
SCOPED_TRACE("Child view " +
base::NumberToString(
widget->GetContentsView()->GetIndexOf(view).value()));
view->RequestFocus();
EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
EXPECT_FALSE(::IsWindowEnabled(hwnd));
EXPECT_EQ(hwnd, ::GetActiveWindow());
EXPECT_TRUE(widget->IsActive());
EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
EXPECT_TRUE(::IsWindowEnabled(hwnd));
EXPECT_EQ(hwnd, ::GetActiveWindow());
EXPECT_TRUE(widget->IsActive());
EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
}
}
class WidgetActivationTest : public Widget {
public:
WidgetActivationTest() = default;
WidgetActivationTest(const WidgetActivationTest&) = delete;
WidgetActivationTest& operator=(const WidgetActivationTest&) = delete;
~WidgetActivationTest() override = default;
bool OnNativeWidgetActivationChanged(bool active) override {
active_ = active;
return true;
}
bool active() const { return active_; }
private:
bool active_ = false;
};
TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
auto widget1 = std::make_unique<WidgetActivationTest>();
Widget::InitParams init_params =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
init_params.native_widget = new DesktopNativeWidgetAura(widget1.get());
init_params.bounds = gfx::Rect(0, 0, 200, 200);
widget1->Init(std::move(init_params));
widget1->Show();
EXPECT_EQ(true, widget1->active());
auto widget2 = std::make_unique<WidgetActivationTest>();
init_params = CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
init_params.native_widget = new DesktopNativeWidgetAura(widget2.get());
widget2->Init(std::move(init_params));
widget2->Show();
EXPECT_EQ(true, widget2->active());
EXPECT_EQ(false, widget1->active());
HWND win32_native_window1 = HWNDForWidget(widget1.get());
EXPECT_TRUE(::IsWindow(win32_native_window1));
::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
EXPECT_EQ(false, widget1->active());
EXPECT_EQ(true, widget2->active());
::SetActiveWindow(win32_native_window1);
EXPECT_EQ(true, widget1->active());
EXPECT_EQ(false, widget2->active());
}
TEST_F(WidgetTestInteractive, FullscreenBoundsReducedOnActivationLoss) {
auto widget1 = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params.native_widget = new DesktopNativeWidgetAura(widget1.get());
widget1->Init(std::move(params));
widget1->SetBounds(gfx::Rect(0, 0, 200, 200));
widget1->Show();
widget1->Activate();
RunPendingMessages();
EXPECT_EQ(::GetActiveWindow(),
widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
widget1->SetFullscreen(true);
EXPECT_TRUE(widget1->IsFullscreen());
RunPendingMessages();
EXPECT_EQ(::GetActiveWindow(),
widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
gfx::Rect fullscreen_bounds = widget1->GetWindowBoundsInScreen();
auto widget2 = std::make_unique<Widget>();
params = CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW);
params.native_widget = new DesktopNativeWidgetAura(widget2.get());
widget2->Init(std::move(params));
widget2->SetBounds(gfx::Rect(0, 0, 200, 200));
widget2->Show();
widget2->Activate();
RunPendingMessages();
EXPECT_EQ(::GetActiveWindow(),
widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
gfx::Rect fullscreen_bounds_after_activation_loss =
widget1->GetWindowBoundsInScreen();
EXPECT_EQ(fullscreen_bounds.height() -
fullscreen_bounds_after_activation_loss.height(),
1);
widget1->Activate();
RunPendingMessages();
EXPECT_EQ(::GetActiveWindow(),
widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
gfx::Rect fullscreen_bounds_after_activate =
widget1->GetWindowBoundsInScreen();
EXPECT_EQ(fullscreen_bounds, fullscreen_bounds_after_activate);
}
TEST_F(WidgetTestInteractive, FullscreenMaximizedWindowBounds) {
auto widget = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params.native_widget = new DesktopNativeWidgetAura(widget.get());
widget->set_frame_type(Widget::FrameType::kForceCustom);
widget->Init(std::move(params));
widget->SetBounds(gfx::Rect(0, 0, 200, 200));
widget->Show();
widget->Maximize();
EXPECT_TRUE(widget->IsMaximized());
widget->SetFullscreen(true);
EXPECT_TRUE(widget->IsFullscreen());
EXPECT_FALSE(widget->IsMaximized());
RunPendingMessages();
aura::WindowTreeHost* host = widget->GetNativeWindow()->GetHost();
HWND hwnd = host->GetAcceleratedWidget();
HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
ASSERT_TRUE(!!monitor);
MONITORINFO monitor_info;
monitor_info.cbSize = sizeof(monitor_info);
ASSERT_TRUE(::GetMonitorInfo(monitor, &monitor_info));
gfx::Rect monitor_bounds(monitor_info.rcMonitor);
gfx::Rect window_bounds = widget->GetWindowBoundsInScreen();
gfx::Rect client_area_bounds = host->GetBoundsInPixels();
EXPECT_EQ(window_bounds, monitor_bounds);
EXPECT_EQ(monitor_bounds, client_area_bounds);
widget->SetFullscreen(false);
EXPECT_FALSE(widget->IsFullscreen());
EXPECT_TRUE(widget->IsMaximized());
client_area_bounds = host->GetBoundsInPixels();
EXPECT_TRUE(monitor_bounds.Contains(client_area_bounds));
EXPECT_NE(monitor_bounds, client_area_bounds);
}
#endif
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
TEST_F(DesktopWidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
TestNativeViewFocusChangeListener focus_listener;
NativeViewFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
const std::vector<gfx::NativeView>& focus_changes =
focus_listener.focus_changes();
auto top_level_widget = std::make_unique<Widget>();
Widget::InitParams init_params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
init_params.show_state = ui::mojom::WindowShowState::kNormal;
gfx::Rect initial_bounds(0, 0, 500, 500);
init_params.bounds = initial_bounds;
top_level_widget->Init(std::move(init_params));
ShowSync(top_level_widget.get());
gfx::NativeView top_level_native_view = top_level_widget->GetNativeView();
ASSERT_FALSE(focus_listener.focus_changes().empty());
EXPECT_EQ(1u, focus_changes.size());
EXPECT_EQ(top_level_native_view, focus_changes[0]);
auto dialog_delegate =
std::make_unique<DialogDelegateView>(DialogDelegateView::CreatePassKey());
dialog_delegate->SetModalType(ui::mojom::ModalType::kWindow);
Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
dialog_delegate.release(), gfx::NativeWindow(),
top_level_widget->GetNativeView());
modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
modal_dialog_widget->Show();
gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
ASSERT_EQ(3u, focus_changes.size());
EXPECT_EQ(gfx::NativeView(), focus_changes[1]);
EXPECT_EQ(modal_native_view, focus_changes[2]);
#if BUILDFLAG(IS_MAC)
modal_dialog_widget->Close();
views::test::WaitForWidgetActive(top_level_widget.get(), true);
#else
views::test::WidgetDestroyedWaiter waiter(modal_dialog_widget);
modal_dialog_widget->Close();
waiter.Wait();
#endif
ASSERT_EQ(5u, focus_changes.size());
EXPECT_EQ(gfx::NativeView(), focus_changes[3]);
EXPECT_EQ(top_level_native_view, focus_changes[4]);
top_level_widget->Close();
NativeViewFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
}
#endif
TEST_F(DesktopWidgetTestInteractive, CanActivateFlagIsHonored) {
auto widget = std::make_unique<Widget>();
Widget::InitParams init_params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
init_params.bounds = gfx::Rect(0, 0, 200, 200);
init_params.activatable = Widget::InitParams::Activatable::kNo;
widget->Init(std::move(init_params));
widget->Show();
EXPECT_FALSE(widget->IsActive());
}
#if defined(USE_AURA)
TEST_F(DesktopWidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
widget->SetBounds(gfx::Rect(0, 0, 200, 200));
std::unique_ptr<Textfield> textfield = CreateTextfield();
auto* const textfield_ptr = textfield.get();
textfield_ptr->SetBounds(0, 0, 200, 20);
textfield_ptr->SetText(u"some text");
widget->GetRootView()->AddChildView(std::move(textfield));
ShowSync(widget.get());
textfield_ptr->RequestFocus();
textfield_ptr->SelectAll(true);
TextfieldTestApi textfield_test_api(textfield_ptr);
ui::test::EventGenerator generator(GetRootWindow(widget.get()));
generator.GestureTapAt(textfield_ptr->GetBoundsInScreen().origin() +
gfx::Vector2d(10, 10));
ASSERT_TRUE(textfield_test_api.touch_selection_controller());
static_cast<TouchSelectionControllerImpl*>(
textfield_test_api.touch_selection_controller())
->ShowQuickMenuImmediatelyForTesting();
EXPECT_TRUE(textfield_ptr->HasFocus());
EXPECT_TRUE(widget->IsActive());
EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
}
#endif
#if BUILDFLAG(IS_WIN)
TEST_F(DesktopWidgetTestInteractive, DisableViewDoesNotActivateWidget) {
#else
TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
#endif
auto widget1 = std::make_unique<Widget>();
Widget::InitParams params1 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_POPUP);
params1.activatable = Widget::InitParams::Activatable::kYes;
widget1->Init(std::move(params1));
View* view1 = widget1->GetRootView()->AddChildView(std::make_unique<View>());
view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget1->Show();
ActivateSync(widget1.get());
FocusManager* focus_manager1 = widget1->GetFocusManager();
ASSERT_TRUE(focus_manager1);
focus_manager1->SetFocusedView(view1);
EXPECT_EQ(view1, focus_manager1->GetFocusedView());
auto widget2 = std::make_unique<Widget>();
Widget::InitParams params2 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_POPUP);
params2.activatable = Widget::InitParams::Activatable::kYes;
widget2->Init(std::move(params2));
View* view2 = widget2->GetRootView()->AddChildView(std::make_unique<View>());
view2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget2->Show();
ActivateSync(widget2.get());
EXPECT_TRUE(widget2->IsActive());
EXPECT_FALSE(widget1->IsActive());
FocusManager* focus_manager2 = widget2->GetFocusManager();
ASSERT_TRUE(focus_manager2);
focus_manager2->SetFocusedView(view2);
EXPECT_EQ(view2, focus_manager2->GetFocusedView());
view1->SetEnabled(false);
EXPECT_NE(view1, focus_manager1->GetFocusedView());
EXPECT_FALSE(widget1->IsActive());
EXPECT_TRUE(widget2->IsActive());
}
TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(widget.get());
EXPECT_EQ(GetWidgetShowState(widget.get()),
ui::mojom::WindowShowState::kNormal);
}
TEST_F(WidgetTestInteractive, ShowInactive) {
WidgetTest::WaitForSystemAppActivation();
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowInactiveSync(widget.get());
EXPECT_EQ(GetWidgetShowState(widget.get()),
ui::mojom::WindowShowState::kInactive);
}
TEST_F(WidgetTestInteractive, InactiveBeforeShow) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
EXPECT_FALSE(widget->IsActive());
EXPECT_FALSE(widget->IsVisible());
ShowSync(widget.get());
EXPECT_TRUE(widget->IsActive());
EXPECT_TRUE(widget->IsVisible());
}
TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
std::unique_ptr<Widget> widget2 = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(widget2.get());
EXPECT_FALSE(widget->IsActive());
EXPECT_TRUE(widget2->IsVisible());
EXPECT_TRUE(widget2->IsActive());
ShowSync(widget.get());
EXPECT_TRUE(widget->IsActive());
EXPECT_FALSE(widget2->IsActive());
ShowInactiveSync(widget.get());
EXPECT_TRUE(widget->IsActive());
EXPECT_FALSE(widget2->IsActive());
EXPECT_EQ(GetWidgetShowState(widget.get()),
ui::mojom::WindowShowState::kNormal);
}
TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
widget->SetBounds(gfx::Rect(100, 100, 100, 100));
ShowInactiveSync(widget.get());
ShowSync(widget.get());
EXPECT_EQ(GetWidgetShowState(widget.get()),
ui::mojom::WindowShowState::kNormal);
}
TEST_F(WidgetTestInteractive, WidgetShouldBeActiveWhenShow) {
std::unique_ptr<Widget> anchor_widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
anchor_widget->Show();
test::WaitForWidgetActive(anchor_widget.get(), true);
EXPECT_TRUE(anchor_widget->IsActive());
#if !BUILDFLAG(IS_MAC)
EXPECT_TRUE(anchor_widget->GetNativeWindow()->HasFocus());
#endif
}
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(widget.get());
EXPECT_EQ(GetWidgetShowState(widget.get()),
ui::mojom::WindowShowState::kNormal);
auto widget2 = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_POPUP);
widget2->Init(std::move(params));
widget2->Show();
RunPendingMessagesForActiveStatusChange();
EXPECT_EQ(GetWidgetShowState(widget2.get()),
ui::mojom::WindowShowState::kInactive);
EXPECT_EQ(GetWidgetShowState(widget.get()),
ui::mojom::WindowShowState::kNormal);
}
#endif
#if BUILDFLAG(IS_MAC)
#define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
#else
#define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
#endif
TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
std::unique_ptr<Widget> toplevel = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
toplevel->Show();
RunPendingMessages();
EXPECT_EQ(ui::mojom::WindowShowState::kNormal,
GetWidgetShowState(toplevel.get()));
toplevel->SetFullscreen(true);
EXPECT_EQ(ui::mojom::WindowShowState::kFullscreen,
GetWidgetShowState(toplevel.get()));
toplevel->SetFullscreen(false);
EXPECT_NE(ui::mojom::WindowShowState::kFullscreen,
GetWidgetShowState(toplevel.get()));
EXPECT_EQ(ui::mojom::WindowShowState::kNormal,
GetWidgetShowState(toplevel.get()));
toplevel->Maximize();
EXPECT_EQ(ui::mojom::WindowShowState::kMaximized,
GetWidgetShowState(toplevel.get()));
toplevel->SetFullscreen(true);
EXPECT_EQ(ui::mojom::WindowShowState::kFullscreen,
GetWidgetShowState(toplevel.get()));
toplevel->SetFullscreen(false);
EXPECT_NE(ui::mojom::WindowShowState::kFullscreen,
GetWidgetShowState(toplevel.get()));
EXPECT_EQ(ui::mojom::WindowShowState::kMaximized,
GetWidgetShowState(toplevel.get()));
}
TEST_F(WidgetTestInteractive, InitialFocus) {
std::unique_ptr<Widget> toplevel = base::WrapUnique(
CreateTopLevelPlatformWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
View* view = new View;
view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
toplevel->GetContentsView()->AddChildViewRaw(view);
ShowSync(toplevel.get());
toplevel->Show();
EXPECT_FALSE(view->HasFocus());
EXPECT_FALSE(toplevel->GetFocusManager()->GetStoredFocusView());
toplevel->CloseNow();
TestInitialFocusWidgetDelegate delegate(GetContext());
Widget* widget = delegate.GetWidget();
ShowSync(widget);
widget->Show();
EXPECT_TRUE(delegate.view()->HasFocus());
EXPECT_EQ(delegate.view(), widget->GetFocusManager()->GetStoredFocusView());
}
TEST_F(DesktopWidgetTestInteractive, RestoreAfterMinimize) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(widget.get());
ASSERT_FALSE(widget->IsMinimized());
PropertyWaiter minimize_waiter(
base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
true);
widget->Minimize();
EXPECT_TRUE(minimize_waiter.Wait());
PropertyWaiter restore_waiter(
base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
false);
widget->Restore();
EXPECT_TRUE(restore_waiter.Wait());
}
#if !BUILDFLAG(IS_MAC)
TEST_F(DesktopWidgetTestInteractive, ShowAfterMaximize) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(widget.get());
ASSERT_FALSE(widget->IsMaximized());
PropertyWaiter maximize_waiter(
base::BindRepeating(&Widget::IsMaximized, base::Unretained(widget.get())),
true);
widget->Maximize();
EXPECT_TRUE(maximize_waiter.Wait());
ShowSync(widget.get());
EXPECT_TRUE(widget->IsMaximized());
ShowInactiveSync(widget.get());
EXPECT_TRUE(widget->IsMaximized());
}
#endif
#if BUILDFLAG(IS_WIN)
TEST_F(DesktopWidgetTestInteractive, RestoreAndMinimizeVisibility) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
aura::Window* root_window = GetRootWindow(widget.get());
ShowSync(widget.get());
ASSERT_FALSE(widget->IsMinimized());
EXPECT_TRUE(root_window->IsVisible());
PropertyWaiter minimize_widget_waiter(
base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
true);
widget->Minimize();
EXPECT_TRUE(minimize_widget_waiter.Wait());
EXPECT_TRUE(widget->IsVisible());
EXPECT_FALSE(root_window->IsVisible());
PropertyWaiter restore_widget_waiter(
base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
false);
widget->Restore();
EXPECT_TRUE(restore_widget_waiter.Wait());
EXPECT_TRUE(widget->IsVisible());
EXPECT_TRUE(root_window->IsVisible());
}
TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
aura::Window* root_window = GetRootWindow(widget.get());
auto* widget_window = widget->GetNativeWindow();
ShowSync(widget.get());
ASSERT_FALSE(widget->IsMinimized());
EXPECT_TRUE(root_window->IsVisible());
widget_window->Focus();
EXPECT_TRUE(widget_window->HasFocus());
widget->GetContentsView()->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget->GetContentsView()->RequestFocus();
EXPECT_TRUE(widget->GetContentsView()->HasFocus());
PropertyWaiter minimize_widget_waiter(
base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
true);
widget->Minimize();
EXPECT_TRUE(minimize_widget_waiter.Wait());
EXPECT_TRUE(widget->IsVisible());
EXPECT_FALSE(root_window->IsVisible());
PropertyWaiter restore_widget_waiter(
base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
false);
widget->Activate();
EXPECT_TRUE(widget->GetContentsView()->HasFocus());
EXPECT_TRUE(restore_widget_waiter.Wait());
EXPECT_TRUE(widget->IsVisible());
EXPECT_TRUE(root_window->IsVisible());
EXPECT_TRUE(widget_window->CanFocus());
}
class SyntheticMouseMoveCounter : public ui::EventHandler {
public:
explicit SyntheticMouseMoveCounter(Widget* widget) : widget_(widget) {
widget_->GetNativeWindow()->AddPreTargetHandler(this);
}
SyntheticMouseMoveCounter(const SyntheticMouseMoveCounter&) = delete;
SyntheticMouseMoveCounter& operator=(const SyntheticMouseMoveCounter&) =
delete;
~SyntheticMouseMoveCounter() override {
widget_->GetNativeWindow()->RemovePreTargetHandler(this);
}
void OnMouseEvent(ui::MouseEvent* event) override {
if (event->type() == ui::EventType::kMouseMoved && event->IsSynthesized()) {
++count_;
}
}
int num_synthetic_mouse_moves() const { return count_; }
private:
int count_ = 0;
raw_ptr<Widget> widget_;
};
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
TEST_F(DesktopWidgetTestInteractive,
DISABLED_DoNotSynthesizeMouseMoveOnVisibilityChangeIfOccluded) {
std::unique_ptr<Widget> widget_below =
base::WrapUnique(CreateTopLevelPlatformDesktopWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
widget_below->SetBounds(gfx::Rect(300, 300));
widget_below->Show();
base::RunLoop run_loop;
ui_controls::SendMouseMoveNotifyWhenDone(150, 150, run_loop.QuitClosure());
run_loop.Run();
auto child = std::make_unique<Widget>();
Widget::InitParams child_params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
child_params.parent = widget_below->GetNativeView();
child_params.context = widget_below->GetNativeWindow();
child->Init(std::move(child_params));
child->SetBounds(gfx::Rect(300, 300));
child->Show();
base::RunLoop().RunUntilIdle();
SyntheticMouseMoveCounter counter_below(widget_below.get());
EXPECT_EQ(0, counter_below.num_synthetic_mouse_moves());
child->Hide();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, counter_below.num_synthetic_mouse_moves());
std::unique_ptr<Widget> widget_above =
base::WrapUnique(CreateTopLevelPlatformDesktopWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
widget_above->SetBounds(gfx::Rect(300, 300));
widget_above->Show();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(widget_above->AsWidget()->IsStackedAbove(
widget_below->AsWidget()->GetNativeView()));
child->Show();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, counter_below.num_synthetic_mouse_moves());
}
#endif
#endif
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
ShowSync(widget.get());
ASSERT_FALSE(widget->IsMinimized());
View mouse_handler_view;
internal::RootView* root_view =
static_cast<internal::RootView*>(widget->GetRootView());
root_view->SetMouseAndGestureHandler(&mouse_handler_view);
EXPECT_TRUE(GetGestureHandler(root_view));
widget->Minimize();
{
views::test::PropertyWaiter minimize_waiter(
base::BindRepeating(&Widget::IsMinimized,
base::Unretained(widget.get())),
true);
EXPECT_TRUE(minimize_waiter.Wait());
}
EXPECT_FALSE(GetGestureHandler(root_view));
}
#endif
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
BUILDFLAG(ENABLE_DESKTOP_AURA)
TEST_F(DesktopWidgetTestInteractive,
DesktopNativeWidgetWithModalTransientChild) {
WidgetAutoclosePtr deactivate_widget(CreateTopLevelNativeWidget());
ShowSync(deactivate_widget.get());
WidgetAutoclosePtr top_level(CreateTopLevelNativeWidget());
std::unique_ptr<Textfield> textfield = CreateTextfield();
auto* const textfield_ptr = textfield.get();
textfield_ptr->SetBounds(0, 0, 200, 20);
top_level->GetRootView()->AddChildView(std::move(textfield));
ShowSync(top_level.get());
textfield_ptr->RequestFocus();
EXPECT_TRUE(textfield_ptr->HasFocus());
auto dialog_delegate =
std::make_unique<DialogDelegateView>(DialogDelegateView::CreatePassKey());
dialog_delegate->SetModalType(ui::mojom::ModalType::kWindow);
Widget* modal_dialog_widget = DialogDelegate::CreateDialogWidget(
dialog_delegate.release(), nullptr, top_level->GetNativeView());
modal_dialog_widget->SetBounds(gfx::Rect(0, 0, 100, 10));
std::unique_ptr<Textfield> dialog_textfield = CreateTextfield();
auto* const dialog_textfield_ptr = dialog_textfield.get();
dialog_textfield_ptr->SetBounds(0, 0, 50, 5);
modal_dialog_widget->GetRootView()->AddChildView(std::move(dialog_textfield));
modal_dialog_widget->Show();
dialog_textfield_ptr->RequestFocus();
EXPECT_TRUE(dialog_textfield_ptr->HasFocus());
EXPECT_FALSE(textfield_ptr->HasFocus());
DeactivateSync(top_level.get());
EXPECT_FALSE(dialog_textfield_ptr->HasFocus());
EXPECT_FALSE(textfield_ptr->HasFocus());
ActivateSync(top_level.get());
EXPECT_TRUE(dialog_textfield_ptr->HasFocus());
EXPECT_FALSE(textfield_ptr->HasFocus());
}
#endif
TEST_F(WidgetTestInteractive, NativeViewRemainsValidPostClose) {
WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
EXPECT_TRUE(toplevel->GetNativeView());
toplevel->Close();
EXPECT_TRUE(toplevel->IsClosed());
EXPECT_TRUE(toplevel->GetNativeView());
}
namespace {
class CaptureLostState {
public:
CaptureLostState() = default;
CaptureLostState(const CaptureLostState&) = delete;
CaptureLostState& operator=(const CaptureLostState&) = delete;
bool GetAndClearGotCaptureLost() {
bool value = got_capture_lost_;
got_capture_lost_ = false;
return value;
}
void OnMouseCaptureLost() { got_capture_lost_ = true; }
private:
bool got_capture_lost_ = false;
};
class CaptureLostTrackingWidget : public Widget {
public:
explicit CaptureLostTrackingWidget(CaptureLostState* capture_lost_state)
: capture_lost_state_(capture_lost_state) {}
CaptureLostTrackingWidget(const CaptureLostTrackingWidget&) = delete;
CaptureLostTrackingWidget& operator=(const CaptureLostTrackingWidget&) =
delete;
void OnMouseCaptureLost() override {
capture_lost_state_->OnMouseCaptureLost();
Widget::OnMouseCaptureLost();
}
private:
raw_ptr<CaptureLostState> capture_lost_state_;
};
}
class WidgetCaptureTest : public DesktopWidgetTestInteractive {
public:
WidgetCaptureTest() = default;
WidgetCaptureTest(const WidgetCaptureTest&) = delete;
WidgetCaptureTest& operator=(const WidgetCaptureTest&) = delete;
~WidgetCaptureTest() override = default;
void TestCapture(bool use_desktop_native_widget) {
std::unique_ptr<Widget> widget1 =
std::make_unique<CaptureLostTrackingWidget>(capture_state1_.get());
InitPlatformWidget(widget1.get(), use_desktop_native_widget);
widget1->Show();
std::unique_ptr<Widget> widget2 =
std::make_unique<CaptureLostTrackingWidget>(capture_state2_.get());
InitPlatformWidget(widget2.get(), use_desktop_native_widget);
widget2->Show();
widget2->SetCapture(widget2->GetRootView());
EXPECT_FALSE(widget1->HasCapture());
EXPECT_TRUE(widget2->HasCapture());
EXPECT_FALSE(capture_state1_->GetAndClearGotCaptureLost());
EXPECT_FALSE(capture_state2_->GetAndClearGotCaptureLost());
widget1->SetCapture(widget1->GetRootView());
EXPECT_TRUE(widget1->HasCapture());
EXPECT_FALSE(widget2->HasCapture());
EXPECT_FALSE(capture_state1_->GetAndClearGotCaptureLost());
EXPECT_TRUE(capture_state2_->GetAndClearGotCaptureLost());
widget1->ReleaseCapture();
EXPECT_FALSE(widget1->HasCapture());
EXPECT_FALSE(widget2->HasCapture());
EXPECT_TRUE(capture_state1_->GetAndClearGotCaptureLost());
EXPECT_FALSE(capture_state2_->GetAndClearGotCaptureLost());
}
void InitPlatformWidget(Widget* widget, bool use_desktop_native_widget) {
Widget::InitParams params =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW);
params.native_widget =
use_desktop_native_widget
? nullptr
: CreatePlatformNativeWidgetImpl(widget, kDefault, nullptr);
widget->Init(std::move(params));
}
protected:
void SetUp() override {
DesktopWidgetTestInteractive::SetUp();
capture_state1_ = std::make_unique<CaptureLostState>();
capture_state2_ = std::make_unique<CaptureLostState>();
}
private:
std::unique_ptr<CaptureLostState> capture_state1_;
std::unique_ptr<CaptureLostState> capture_state2_;
};
TEST_F(WidgetCaptureTest, Capture) {
TestCapture(false);
}
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
TestCapture(true);
}
#endif
TEST_F(WidgetCaptureTest, DestroyWithCapture_CloseNow) {
CaptureLostState capture_state;
auto widget = std::make_unique<CaptureLostTrackingWidget>(&capture_state);
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
widget->Init(std::move(params));
widget->Show();
widget->SetCapture(widget->GetRootView());
EXPECT_TRUE(widget->HasCapture());
EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
widget->CloseNow();
EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost());
}
TEST_F(WidgetCaptureTest, DestroyWithCapture_Close) {
CaptureLostState capture_state;
auto widget = std::make_unique<CaptureLostTrackingWidget>(&capture_state);
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
widget->Init(std::move(params));
widget->Show();
widget->SetCapture(widget->GetRootView());
EXPECT_TRUE(widget->HasCapture());
EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
widget->Close();
EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost());
}
TEST_F(WidgetCaptureTest, DestroyWithCapture_ClientOwnsWidget) {
Widget widget;
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
widget.Init(std::move(params));
widget.Show();
widget.SetCapture(widget.GetRootView());
EXPECT_TRUE(widget.HasCapture());
}
TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
auto widget = std::make_unique<Widget>();
Widget::InitParams params =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.bounds = gfx::Rect(400, 400);
widget->Init(std::move(params));
auto contents_view = std::make_unique<View>();
MouseView* mouse_view1 =
contents_view->AddChildView(std::make_unique<MouseView>());
MouseView* mouse_view2 =
contents_view->AddChildView(std::make_unique<MouseView>());
widget->SetContentsView(std::move(contents_view));
mouse_view1->SetBounds(0, 0, 200, 400);
mouse_view2->SetBounds(200, 0, 200, 400);
widget->SetCapture(mouse_view1);
EXPECT_FALSE(widget->HasCapture());
widget->Show();
ui::test::EventGenerator generator(GetRootWindow(widget.get()),
widget->GetNativeWindow());
generator.set_current_screen_location(
widget->GetClientAreaBoundsInScreen().CenterPoint());
generator.PressLeftButton();
EXPECT_FALSE(mouse_view1->pressed());
EXPECT_TRUE(mouse_view2->pressed());
}
TEST_F(WidgetCaptureTest, CaptureAutoReset) {
std::unique_ptr<Widget> toplevel =
base::WrapUnique(CreateTopLevelFramelessPlatformWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
toplevel->SetContentsView(std::make_unique<View>());
EXPECT_FALSE(toplevel->HasCapture());
toplevel->SetCapture(nullptr);
EXPECT_TRUE(toplevel->HasCapture());
gfx::Point click_location(45, 15);
ui::MouseEvent release(ui::EventType::kMouseReleased, click_location,
click_location, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
toplevel->OnMouseEvent(&release);
EXPECT_FALSE(toplevel->HasCapture());
toplevel->set_auto_release_capture(false);
toplevel->SetCapture(nullptr);
EXPECT_TRUE(toplevel->HasCapture());
toplevel->OnMouseEvent(&release);
EXPECT_TRUE(toplevel->HasCapture());
toplevel->ReleaseCapture();
EXPECT_FALSE(toplevel->HasCapture());
}
TEST_F(WidgetCaptureTest, ResetCaptureOnGestureEnd) {
std::unique_ptr<Widget> toplevel =
base::WrapUnique(CreateTopLevelFramelessPlatformWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
View* container = toplevel->SetContentsView(std::make_unique<View>());
View* gesture = new GestureCaptureView;
gesture->SetBounds(0, 0, 30, 30);
container->AddChildViewRaw(gesture);
MouseView* mouse = new MouseView;
mouse->SetBounds(30, 0, 30, 30);
container->AddChildViewRaw(mouse);
toplevel->SetSize(gfx::Size(100, 100));
toplevel->Show();
ui::GestureEvent tap_down(
15, 15, 0, base::TimeTicks(),
ui::GestureEventDetails(ui::EventType::kGestureTapDown));
ui::GestureEvent end(15, 15, 0, base::TimeTicks(),
ui::GestureEventDetails(ui::EventType::kGestureEnd));
toplevel->OnGestureEvent(&tap_down);
gfx::Point click_location(45, 15);
ui::MouseEvent press(ui::EventType::kMousePressed, click_location,
click_location, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
ui::MouseEvent release(ui::EventType::kMouseReleased, click_location,
click_location, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
EXPECT_TRUE(toplevel->HasCapture());
toplevel->OnMouseEvent(&press);
toplevel->OnMouseEvent(&release);
EXPECT_EQ(0, mouse->pressed());
EXPECT_FALSE(toplevel->HasCapture());
toplevel->OnGestureEvent(&end);
toplevel->OnMouseEvent(&press);
toplevel->OnMouseEvent(&release);
EXPECT_EQ(1, mouse->pressed());
}
TEST_F(WidgetCaptureTest, DisableCaptureWidgetFromMousePress) {
std::unique_ptr<Widget> first =
base::WrapUnique(CreateTopLevelFramelessPlatformWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
std::unique_ptr<Widget> second =
base::WrapUnique(CreateTopLevelFramelessPlatformWidget(
Widget::InitParams::CLIENT_OWNS_WIDGET));
Widget* second_ptr = second.get();
NestedLoopCaptureView* container = first->SetContentsView(
std::make_unique<NestedLoopCaptureView>(std::move(second)));
second_ptr->SetContentsView(
std::make_unique<ExitLoopOnRelease>(container->GetQuitClosure()));
first->SetSize(gfx::Size(100, 100));
first->Show();
gfx::Point location(20, 20);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&Widget::OnMouseEvent, base::Unretained(second_ptr),
base::Owned(new ui::MouseEvent(
ui::EventType::kMouseReleased, location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON))));
ui::MouseEvent press(ui::EventType::kMousePressed, location, location,
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
first->OnMouseEvent(&press);
EXPECT_FALSE(first->HasCapture());
}
TEST_F(WidgetCaptureTest, GrabUngrab) {
std::unique_ptr<Widget> top_level =
CreateTestWidget(Widget::InitParams::CLIENT_OWNS_WIDGET);
top_level->SetContentsView(std::make_unique<MouseView>());
auto child1 = std::make_unique<Widget>();
Widget::InitParams params1 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_CONTROL);
params1.parent = top_level->GetNativeView();
params1.bounds = gfx::Rect(10, 10, 100, 100);
child1->Init(std::move(params1));
child1->SetContentsView(std::make_unique<MouseView>());
auto child2 = std::make_unique<Widget>();
Widget::InitParams params2 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_CONTROL);
params2.parent = top_level->GetNativeView();
params2.bounds = gfx::Rect(110, 10, 100, 100);
child2->Init(std::move(params2));
child2->SetContentsView(std::make_unique<MouseView>());
top_level->Show();
RunPendingMessages();
ui::test::EventGenerator generator(GetRootWindow(top_level.get()),
child1->GetNativeWindow());
generator.set_current_screen_location(
child1->GetClientAreaBoundsInScreen().CenterPoint());
generator.PressLeftButton();
EXPECT_FALSE(top_level->HasCapture());
EXPECT_TRUE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
generator.ReleaseLeftButton();
EXPECT_FALSE(top_level->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
generator.SetTargetWindow(child2->GetNativeWindow());
generator.set_current_screen_location(
child2->GetClientAreaBoundsInScreen().CenterPoint());
generator.PressLeftButton();
EXPECT_FALSE(top_level->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_TRUE(child2->HasCapture());
generator.ReleaseLeftButton();
EXPECT_FALSE(top_level->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
generator.SetTargetWindow(top_level->GetNativeWindow());
generator.set_current_screen_location(
top_level->GetClientAreaBoundsInScreen().origin());
generator.PressLeftButton();
EXPECT_TRUE(top_level->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
generator.ReleaseLeftButton();
EXPECT_FALSE(top_level->HasCapture());
EXPECT_FALSE(child1->HasCapture());
EXPECT_FALSE(child2->HasCapture());
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_SystemModalWindowReleasesCapture \
DISABLED_SystemModalWindowReleasesCapture
#elif BUILDFLAG(IS_CHROMEOS)
#define MAYBE_SystemModalWindowReleasesCapture \
DISABLED_SystemModalWindowReleasesCapture
#else
#define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
#endif
TEST_F(WidgetCaptureTest, MAYBE_SystemModalWindowReleasesCapture) {
TestNativeViewFocusChangeListener focus_listener;
NativeViewFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
auto top_level_widget = std::make_unique<Widget>();
Widget::InitParams init_params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
init_params.show_state = ui::mojom::WindowShowState::kNormal;
gfx::Rect initial_bounds(0, 0, 500, 500);
init_params.bounds = initial_bounds;
top_level_widget->Init(std::move(init_params));
ShowSync(top_level_widget.get());
ASSERT_FALSE(focus_listener.focus_changes().empty());
EXPECT_EQ(top_level_widget->GetNativeView(),
focus_listener.focus_changes().back());
EXPECT_FALSE(top_level_widget->HasCapture());
top_level_widget->SetCapture(nullptr);
EXPECT_TRUE(top_level_widget->HasCapture());
auto dialog_delegate =
std::make_unique<DialogDelegateView>(DialogDelegateView::CreatePassKey());
dialog_delegate->SetModalType(ui::mojom::ModalType::kSystem);
Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
dialog_delegate.release(), gfx::NativeWindow(),
top_level_widget->GetNativeView());
modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
ShowSync(modal_dialog_widget);
EXPECT_FALSE(top_level_widget->HasCapture());
NativeViewFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
}
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
#else
#define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
#endif
TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
auto widget1 = std::make_unique<Widget>();
Widget::InitParams params1 =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget1->Init(std::move(params1));
MouseView* mouse_view1 =
widget1->SetContentsView(std::make_unique<MouseView>());
widget1->Show();
widget1->SetBounds(gfx::Rect(300, 300));
auto widget2 = std::make_unique<Widget>();
Widget::InitParams params2 =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget2->Init(std::move(params2));
widget2->Show();
widget2->SetBounds(gfx::Rect(400, 0, 300, 300));
ui::test::EventGenerator generator(GetRootWindow(widget1.get()));
generator.set_current_screen_location(gfx::Point(100, 100));
generator.MoveMouseBy(0, 0);
EXPECT_EQ(1, mouse_view1->EnteredCalls());
EXPECT_EQ(0, mouse_view1->ExitedCalls());
widget2->SetCapture(nullptr);
EXPECT_EQ(0, mouse_view1->EnteredCalls());
#if BUILDFLAG(IS_WIN)
EXPECT_EQ(0, mouse_view1->ExitedCalls());
#else
EXPECT_EQ(1, mouse_view1->ExitedCalls());
#endif
}
namespace {
class CaptureOnActivationObserver : public WidgetObserver {
public:
explicit CaptureOnActivationObserver(Widget* widget) {
widget_observation_.Observe(widget);
}
CaptureOnActivationObserver(const CaptureOnActivationObserver&) = delete;
CaptureOnActivationObserver& operator=(const CaptureOnActivationObserver&) =
delete;
~CaptureOnActivationObserver() override = default;
void OnWidgetActivationChanged(Widget* widget, bool active) override {
if (active) {
widget->SetCapture(nullptr);
activation_observed_ = true;
}
}
bool activation_observed() const { return activation_observed_; }
private:
bool activation_observed_ = false;
base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this};
};
}
TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
auto toplevel = std::make_unique<Widget>();
Widget::InitParams toplevel_params =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
toplevel->Init(std::move(toplevel_params));
toplevel->Show();
auto child = std::make_unique<Widget>();
Widget::InitParams child_params =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW_FRAMELESS);
child_params.parent = toplevel->GetNativeView();
child_params.context = toplevel->GetNativeWindow();
child->Init(std::move(child_params));
CaptureOnActivationObserver observer(child.get());
child->Show();
#if BUILDFLAG(IS_MAC)
base::RunLoop().RunUntilIdle();
#endif
EXPECT_TRUE(observer.activation_observed());
EXPECT_TRUE(child->HasCapture());
}
TEST_F(WidgetCaptureTest, SetCaptureUpdatesMouseState) {
static constexpr gfx::Rect kBounds(300, 300);
auto widget = std::make_unique<MouseEventWidget>();
widget->Init(CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW));
widget->Show();
widget->SetBounds(kBounds);
auto generator =
std::make_unique<ui::test::EventGenerator>(GetRootWindow(widget.get()));
generator->PressLeftButton();
auto late_init_widget = std::make_unique<MouseEventWidget>();
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_MENU);
late_init_widget->Init(std::move(params));
late_init_widget->SetBounds(kBounds);
generator->ReleaseLeftButton();
MouseEventRootView* widget_view = widget->root_view();
MouseEventRootView* late_init_view = late_init_widget->root_view();
widget_view->reset_counts();
late_init_view->reset_counts();
late_init_widget->Show();
late_init_widget->SetCapture(nullptr);
generator->SetTargetWindow(GetRootWindow(late_init_widget.get()));
generator->MoveMouseBy(10, 10);
EXPECT_EQ(0, late_init_view->dragged());
EXPECT_GT(late_init_view->moved(), 0);
}
#if BUILDFLAG(IS_WIN)
namespace {
class MouseEventTrackingWidget : public Widget {
public:
MouseEventTrackingWidget() = default;
MouseEventTrackingWidget(const MouseEventTrackingWidget&) = delete;
MouseEventTrackingWidget& operator=(const MouseEventTrackingWidget&) = delete;
~MouseEventTrackingWidget() override = default;
bool GetAndClearGotMouseEvent() {
bool value = got_mouse_event_;
got_mouse_event_ = false;
return value;
}
void OnMouseEvent(ui::MouseEvent* event) override {
got_mouse_event_ = true;
Widget::OnMouseEvent(event);
}
private:
bool got_mouse_event_ = false;
};
}
TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
auto widget1 = std::make_unique<MouseEventTrackingWidget>();
Widget::InitParams params1 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params1.bounds = gfx::Rect(0, 0, 200, 200);
params1.native_widget = new DesktopNativeWidgetAura(widget1.get());
widget1->Init(std::move(params1));
widget1->Show();
auto widget2 = std::make_unique<MouseEventTrackingWidget>();
Widget::InitParams params2 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params2.bounds = gfx::Rect(0, 0, 200, 200);
params2.native_widget = new DesktopNativeWidgetAura(widget2.get());
widget2->Init(std::move(params2));
widget2->Show();
widget2->SetCapture(widget2->GetRootView());
EXPECT_FALSE(widget1->HasCapture());
EXPECT_TRUE(widget2->HasCapture());
widget1->GetAndClearGotMouseEvent();
widget2->GetAndClearGotMouseEvent();
ui::MouseEvent mouse_event(ui::EventType::kMouseExited, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(), ui::EF_NONE,
ui::EF_NONE);
ui::EventDispatchDetails details =
widget1->GetNativeWindow()->GetHost()->GetEventSink()->OnEventFromSource(
&mouse_event);
ASSERT_FALSE(details.dispatcher_destroyed);
EXPECT_TRUE(widget1->GetAndClearGotMouseEvent());
EXPECT_FALSE(widget2->GetAndClearGotMouseEvent());
}
#endif
class WidgetInputMethodInteractiveTest : public DesktopWidgetTestInteractive {
public:
WidgetInputMethodInteractiveTest() = default;
WidgetInputMethodInteractiveTest(const WidgetInputMethodInteractiveTest&) =
delete;
WidgetInputMethodInteractiveTest& operator=(
const WidgetInputMethodInteractiveTest&) = delete;
void SetUp() override {
DesktopWidgetTestInteractive::SetUp();
#if BUILDFLAG(IS_WIN)
deactivate_widget_ = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
deactivate_widget_->Show();
#endif
}
void TearDown() override {
if (deactivate_widget_) {
deactivate_widget_->CloseNow();
}
DesktopWidgetTestInteractive::TearDown();
}
private:
std::unique_ptr<Widget> deactivate_widget_;
};
#if BUILDFLAG(IS_MAC)
#define MAYBE_Activation DISABLED_Activation
#else
#define MAYBE_Activation Activation
#endif
TEST_F(WidgetInputMethodInteractiveTest, MAYBE_Activation) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
std::unique_ptr<Textfield> textfield = CreateTextfield();
auto* const textfield_ptr = textfield.get();
widget->GetRootView()->AddChildView(std::move(textfield));
textfield_ptr->RequestFocus();
ShowSync(widget.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
widget->GetInputMethod()->GetTextInputType());
DeactivateSync(widget.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
widget->GetInputMethod()->GetTextInputType());
}
TEST_F(WidgetInputMethodInteractiveTest, OneWindow) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
Textfield* const textfield1 =
widget->GetRootView()->AddChildView(CreateTextfield());
Textfield* const textfield2 =
widget->GetRootView()->AddChildView(CreateTextfield());
textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
ShowSync(widget.get());
textfield1->RequestFocus();
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
widget->GetInputMethod()->GetTextInputType());
textfield2->RequestFocus();
EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
widget->GetInputMethod()->GetTextInputType());
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
DeactivateSync(widget.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
widget->GetInputMethod()->GetTextInputType());
ActivateSync(widget.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
widget->GetInputMethod()->GetTextInputType());
DeactivateSync(widget.get());
textfield1->RequestFocus();
ActivateSync(widget.get());
EXPECT_TRUE(widget->IsActive());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
widget->GetInputMethod()->GetTextInputType());
#endif
}
TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) {
std::unique_ptr<Widget> parent = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
parent->SetBounds(gfx::Rect(100, 100, 100, 100));
std::unique_ptr<Widget> child =
base::WrapUnique(CreateChildNativeWidgetWithParent(
parent.get(), Widget::InitParams::CLIENT_OWNS_WIDGET));
child->SetBounds(gfx::Rect(0, 0, 50, 50));
child->Show();
Textfield* const textfield_parent =
parent->GetRootView()->AddChildView(CreateTextfield());
Textfield* const textfield_child =
child->GetRootView()->AddChildView(CreateTextfield());
textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
ShowSync(parent.get());
EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod());
textfield_parent->RequestFocus();
EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
parent->GetInputMethod()->GetTextInputType());
textfield_child->RequestFocus();
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
parent->GetInputMethod()->GetTextInputType());
#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
DeactivateSync(parent.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
parent->GetInputMethod()->GetTextInputType());
ActivateSync(parent.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
parent->GetInputMethod()->GetTextInputType());
textfield_parent->RequestFocus();
DeactivateSync(parent.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
parent->GetInputMethod()->GetTextInputType());
ActivateSync(parent.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
parent->GetInputMethod()->GetTextInputType());
#endif
}
TEST_F(WidgetInputMethodInteractiveTest, TextField) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
Textfield* const textfield =
widget->GetRootView()->AddChildView(CreateTextfield());
ShowSync(widget.get());
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
widget->GetInputMethod()->GetTextInputType());
textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
widget->GetInputMethod()->GetTextInputType());
textfield->RequestFocus();
EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
widget->GetInputMethod()->GetTextInputType());
textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
widget->GetInputMethod()->GetTextInputType());
textfield->SetReadOnly(true);
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
widget->GetInputMethod()->GetTextInputType());
}
TEST_F(WidgetInputMethodInteractiveTest, AcceleratorInTextfield) {
std::unique_ptr<Widget> widget = base::WrapUnique(
CreateTopLevelNativeWidget(Widget::InitParams::CLIENT_OWNS_WIDGET));
Textfield* const textfield =
widget->GetRootView()->AddChildView(CreateTextfield());
ShowSync(widget.get());
textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
textfield->RequestFocus();
ui::KeyEvent key_event(ui::EventType::kKeyPressed, ui::VKEY_F,
ui::EF_ALT_DOWN);
ui::Accelerator accelerator(key_event);
widget->GetFocusManager()->RegisterAccelerator(
accelerator, ui::AcceleratorManager::kNormalPriority, textfield);
widget->OnKeyEvent(&key_event);
EXPECT_TRUE(key_event.stopped_propagation());
widget->GetFocusManager()->UnregisterAccelerators(textfield);
ui::KeyEvent key_event2(key_event);
widget->OnKeyEvent(&key_event2);
EXPECT_FALSE(key_event2.stopped_propagation());
}
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
class DesktopWidgetDragTestInteractive : public DesktopWidgetTestInteractive,
public WidgetObserver {
public:
DesktopWidgetDragTestInteractive() = default;
DesktopWidgetDragTestInteractive(const DesktopWidgetDragTestInteractive&) =
delete;
DesktopWidgetDragTestInteractive& operator=(
const DesktopWidgetDragTestInteractive&) = delete;
~DesktopWidgetDragTestInteractive() override = default;
protected:
static constexpr gfx::Rect bounds = gfx::Rect(0, 0, 200, 200);
void InitWidget(Widget* widget,
base::OnceClosure on_drag_enter,
base::OnceClosure on_drag_exit,
base::OnceClosure on_capture_lost) {
widget->AddObserver(this);
Widget::InitParams params =
CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
Widget::InitParams::TYPE_WINDOW);
params.native_widget = new DesktopNativeWidgetAura(widget);
params.bounds = bounds;
widget->Init(std::move(params));
auto on_mouse_exit = base::BindLambdaForTesting([]() {
gfx::Point target_location =
aura::Env::GetInstance()->last_mouse_location();
target_location += gfx::Vector2d(1, 1);
EXPECT_TRUE(
ui_controls::SendMouseMove(target_location.x(), target_location.y()));
});
widget->client_view()->AddChildView(std::make_unique<DragView>(
std::move(on_drag_enter), std::move(on_drag_exit),
std::move(on_capture_lost), std::move(on_mouse_exit)));
widget->LayoutRootViewIfNecessary();
ShowSync(widget);
}
void StartDrag() {
gfx::Point start_location(bounds.width() / 2, bounds.height() / 2);
gfx::Point target_location = start_location + gfx::Vector2d(10, 10);
base::RunLoop move_loop;
EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
start_location.x(), start_location.y(), move_loop.QuitClosure()));
move_loop.Run();
base::RunLoop press_loop;
EXPECT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
ui_controls::MouseButton::LEFT, ui_controls::MouseButtonState::DOWN,
press_loop.QuitClosure()));
press_loop.Run();
EXPECT_TRUE(
ui_controls::SendMouseMove(target_location.x(), target_location.y()));
}
void WaitForDragEnd() {
drag_wait_loop_.Run();
EXPECT_TRUE(drag_entered_);
}
bool drag_entered_ = false;
private:
void OnWidgetDragComplete(Widget* widget) override { drag_wait_loop_.Quit(); }
base::RunLoop drag_wait_loop_;
};
#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
#define MAYBE_CancelShellDrag DISABLED_CancelShellDrag
#else
#define MAYBE_CancelShellDrag CancelShellDrag
#endif
TEST_F(DesktopWidgetDragTestInteractive, MAYBE_CancelShellDrag) {
auto widget = std::make_unique<Widget>();
auto cancel = [&]() {
drag_entered_ = true;
widget->CancelShellDrag(widget->client_view());
#if BUILDFLAG(IS_WIN)
gfx::Point target_location =
aura::Env::GetInstance()->last_mouse_location();
target_location += gfx::Vector2d(1, 1);
EXPECT_TRUE(
ui_controls::SendMouseMove(target_location.x(), target_location.y()));
#endif
};
#if BUILDFLAG(IS_WIN)
base::OnceClosure on_capture_lost = base::BindLambdaForTesting(cancel);
#else
base::OnceClosure on_capture_lost = base::DoNothing();
#endif
InitWidget(widget.get(), base::BindLambdaForTesting(cancel),
base::DoNothing(), std::move(on_capture_lost));
StartDrag();
WaitForDragEnd();
}
#if BUILDFLAG(IS_OZONE_X11)
#define MAYBE_RunShellDragUpdatesMouseButtonState \
DISABLED_RunShellDragUpdatesMouseButtonState
#else
#define MAYBE_RunShellDragUpdatesMouseButtonState \
RunShellDragUpdatesMouseButtonState
#endif
TEST_F(DesktopWidgetDragTestInteractive,
MAYBE_RunShellDragUpdatesMouseButtonState) {
#if BUILDFLAG(IS_WIN)
aura::test::EnvTestHelper(aura::Env::GetInstance())
.SetInputStateLookup(aura::InputStateLookup::Create());
#endif
auto widget = std::make_unique<MouseEventWidget>();
auto on_enter = [&]() {
drag_entered_ = true;
EXPECT_TRUE(ui_controls::SendMouseEvents(
ui_controls::MouseButton::LEFT, ui_controls::MouseButtonState::UP));
};
#if BUILDFLAG(IS_WIN)
base::OnceClosure on_capture_lost = base::BindLambdaForTesting([&] {
gfx::Point target_location =
aura::Env::GetInstance()->last_mouse_location();
target_location += gfx::Vector2d(1, 1);
EXPECT_TRUE(
ui_controls::SendMouseMove(target_location.x(), target_location.y()));
});
#else
base::OnceClosure on_capture_lost = base::DoNothing();
#endif
InitWidget(widget.get(), base::BindLambdaForTesting(on_enter),
base::DoNothing(), std::move(on_capture_lost));
StartDrag();
WaitForDragEnd();
MouseEventRootView* root = widget->root_view();
root->reset_counts();
widget->native_widget_private()->SetCapture();
gfx::Point target_location = aura::Env::GetInstance()->last_mouse_location();
target_location += gfx::Vector2d(10, 10);
base::RunLoop move_loop;
EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
target_location.x(), target_location.y(), move_loop.QuitClosure()));
move_loop.Run();
EXPECT_EQ(0, root->dragged());
EXPECT_EQ(1, root->moved());
}
#endif
}