blob: 2ba44d72413b366556f82b6d068a7f9d96bead24 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/stack_trace.h"
#include "base/scoped_observation.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "content/public/test/browser_test.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
namespace {
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kWindowHidden);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kWindowMinimized);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kWindowShown);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kWindowRestored);
} // namespace
// This test class tests assumptions about the order in which events happen when
// a window is minimized and restored, so that they can be handled properly by
// the `ElementTracker` implementation.
//
// A window that is minimized should not be counted as not-visible, even if the
// system believes the window is not visible. These are regression tests to
// ensure that the platform-specific implementations continue to conform to the
// set of cases we are capable of handling.
//
// Notes:
// - Not all platforms change visibility on minimize - Windows, for example,
// does not.
// - The code path for updating minimized state and/or visibility is different
// on each platform.
// - In some cases the order depends on the order of observers.
//
class ElementTrackerViewsMinimizeRestoreUiTest : public InteractiveBrowserTest,
public views::WidgetObserver {
public:
ElementTrackerViewsMinimizeRestoreUiTest() = default;
~ElementTrackerViewsMinimizeRestoreUiTest() override = default;
void SetUpOnMainThread() override {
InteractiveBrowserTest::SetUpOnMainThread();
widget_observation_.Observe(context_widget());
}
void TearDownOnMainThread() override {
widget_observation_.Reset();
InteractiveBrowserTest::TearDownOnMainThread();
}
protected:
views::Widget* GetWidget() { return context_widget(); }
ui::TrackedElement* GetTargetElement() {
return private_test_impl().GetPivotElement(
private_test_impl().default_context());
}
void SendEvent(ui::CustomElementEventType event) {
ui::ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
GetTargetElement(), event);
}
private:
// views::WidgetObserver:
void OnWidgetVisibilityChanged(views::Widget*, bool visible) override {
LOG(INFO) << "------------- Window visibility set to " << visible << "\n"
<< base::debug::StackTrace(20);
if (visible) {
SendEvent(kWindowShown);
} else {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindLambdaForTesting([this]() {
LOG(INFO) << "------------- Window hidden";
SendEvent(kWindowHidden);
}));
}
}
void OnWidgetShowStateChanged(views::Widget* widget) override {
LOG(INFO) << "------------- Window state set to "
<< (widget->IsMinimized() ? "minimized" : "restored") << "\n"
<< base::debug::StackTrace(20);
;
SendEvent(widget->IsMinimized() ? kWindowMinimized : kWindowRestored);
}
base::ScopedObservation<views::Widget, views::WidgetObserver>
widget_observation_{this};
};
IN_PROC_BROWSER_TEST_F(ElementTrackerViewsMinimizeRestoreUiTest,
TestAssumptions) {
// Note: on Windows, visibility does not change on minimization.
#if !BUILDFLAG(IS_WIN)
bool shown = false;
#endif
RunTestSequence(
Check([this]() { return GetWidget()->IsVisible(); }),
Do([this]() { GetWidget()->Minimize(); }),
WithoutDelay(
AfterEvent(ui::test::internal::kInteractiveTestPivotElementId,
kWindowMinimized, []() { LOG(INFO) << "GOT MINIMIZED"; })
#if !BUILDFLAG(IS_WIN)
,
AfterEvent(ui::test::internal::kInteractiveTestPivotElementId,
kWindowHidden, []() { LOG(INFO) << "GOT HIDDEN"; })
#endif
),
Do([this]() { GetWidget()->Restore(); }),
#if BUILDFLAG(IS_WIN)
AfterEvent(ui::test::internal::kInteractiveTestPivotElementId,
kWindowRestored, []() { LOG(INFO) << "GOT RESTORED"; })
#else
InParallel(
RunSubsequence(WithoutDelay(AfterEvent(
ui::test::internal::kInteractiveTestPivotElementId, kWindowShown,
[&]() {
LOG(INFO) << "GOT SHOWN";
shown = true;
}))),
RunSubsequence(WithoutDelay(
AfterEvent(ui::test::internal::kInteractiveTestPivotElementId,
kWindowRestored,
[]() { LOG(INFO) << "GOT RESTORED"; }),
CheckVariable(shown, true))))
#endif
);
}