Commit 04f0abb9 authored by David Bienvenu's avatar David Bienvenu Committed by Commit Bot

Fix re-register win events when restoring minimized window.

If there are no visible app windows, restoring app window wasn't re-registering
events because OnWindowVisiblityChanged notification wasn't generated for root window.

Add unit test for going from visible to hidden to occluded.

Bug: 813093
Change-Id: I11b5fd7c7dc104ec74699c8e66aa06d9954a5977
Reviewed-on: https://chromium-review.googlesource.com/c/1326621
Commit-Queue: David Bienvenu <davidbienvenu@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611322}
parent 7cecfe61
......@@ -168,17 +168,20 @@ bool NativeWindowOcclusionTrackerWin::IsWindowVisibleAndFullyOpaque(
void NativeWindowOcclusionTrackerWin::UpdateOcclusionState(
const base::flat_map<HWND, Window::OcclusionState>&
root_window_hwnds_occlusion_state) {
num_visible_root_windows_ = 0;
for (const auto& root_window_pair : root_window_hwnds_occlusion_state) {
auto it = hwnd_root_window_map_.find(root_window_pair.first);
// The window was destroyed while processing occlusion.
if (it == hwnd_root_window_map_.end())
continue;
Window* root_window = it->second;
// Check Window::IsVisible here, on the UI thread, because it can't be
// checked on the occlusion calculation thread.
bool root_window_hidden = !it->second->IsVisible();
it->second->GetHost()->SetNativeWindowOcclusionState(
!root_window->IsVisible() ? Window::OcclusionState::HIDDEN
root_window_hidden ? Window::OcclusionState::HIDDEN
: root_window_pair.second);
if (!root_window_hidden)
num_visible_root_windows_++;
}
}
......@@ -223,9 +226,12 @@ void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
HandleVisibilityChanged(bool visible) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// May have gone from having no visible windows to having one, in
// which case we need to register event hooks.
if (visible)
// which case we need to register event hooks, and make sure that an
// occlusion calculation is scheduled.
if (visible) {
MaybeRegisterEventHooks();
ScheduleOcclusionCalculationIfNeeded();
}
}
void NativeWindowOcclusionTrackerWin::WindowOcclusionCalculator::
......
......@@ -207,6 +207,9 @@ class AURA_EXPORT NativeWindowOcclusionTrackerWin : public WindowObserver {
// |root_window_hwnds_occlusion_state_|.
base::flat_map<HWND, Window*> hwnd_root_window_map_;
// This is set by UpdateOcclusionState. It is currently only used by tests.
int num_visible_root_windows_ = 0;
std::unique_ptr<WindowOcclusionCalculator> occlusion_calculator_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowOcclusionTrackerWin);
......
......@@ -48,16 +48,20 @@ class MockWindowTreeHostObserver : public WindowTreeHostObserver {
// WindowTreeHostObserver:
void OnOcclusionStateChanged(WindowTreeHost* host,
Window::OcclusionState new_state) override {
EXPECT_NE(new_state, Window::OcclusionState::UNKNOWN);
// Should only get notified when the occlusion state changes.
EXPECT_NE(new_state, cur_state_);
cur_state_ = new_state;
if (cur_state_ == expectation_) {
if (expectation_ != Window::OcclusionState::UNKNOWN &&
cur_state_ == expectation_) {
EXPECT_FALSE(quit_closure_.is_null());
std::move(quit_closure_).Run();
}
}
void set_quit_closure(base::OnceClosure quit_closure) {
quit_closure_ = std::move(quit_closure);
}
void set_expectation(Window::OcclusionState expectation) {
expectation_ = expectation;
}
......@@ -163,6 +167,11 @@ class NativeWindowOcclusionTrackerTest : public test::AuraTestBase {
window->env()->GetWindowOcclusionTracker()->Track(window);
}
int GetNumVisibleRootWindows() {
return NativeWindowOcclusionTrackerWin::GetOrCreateInstance()
->num_visible_root_windows_;
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<TestNativeWindow> native_win_;
......@@ -200,12 +209,60 @@ TEST_F(NativeWindowOcclusionTrackerTest, SimpleHidden) {
MockWindowTreeHostObserver observer(run_loop.QuitClosure());
CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
CreateNativeWindowWithBounds(gfx::Rect(200, 0, 100, 100));
// Minimize the tracked aura window and check that its occlusion state
// Iconify the tracked aura window and check that its occlusion state
// is HIDDEN.
::ShowWindow(host()->GetAcceleratedWidget(), SW_MINIMIZE);
CloseWindow(host()->GetAcceleratedWidget());
observer.set_expectation(Window::OcclusionState::HIDDEN);
run_loop.Run();
EXPECT_FALSE(observer.is_expecting_call());
}
// Test that minimizing and restoring an app window results in the occlusion
// tracker re-registering for win events and detecting that a native window
// occludes the app window.
TEST_F(NativeWindowOcclusionTrackerTest, OcclusionAfterVisibilityToggle) {
base::RunLoop run_loop;
MockWindowTreeHostObserver observer(run_loop.QuitClosure());
CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
observer.set_expectation(Window::OcclusionState::VISIBLE);
run_loop.Run();
base::RunLoop run_loop2;
observer.set_expectation(Window::OcclusionState::HIDDEN);
observer.set_quit_closure(run_loop2.QuitClosure());
// host()->window()->Hide() is needed to generate OnWindowVisibilityChanged
// notifications.
host()->window()->Hide();
// This makes the window iconic.
::CloseWindow(host()->GetAcceleratedWidget());
run_loop2.Run();
// HIDDEN state is set synchronously by OnWindowVsiblityChanged notification,
// before occlusion is calculated, so the above expectation will be met w/o an
// occlusion calculation.
// Loop until an occlusion calculation has run with no non-hidden app windows.
do {
// Need to pump events in order for UpdateOcclusionState to get called, and
// update the number of non hidden root windows. When that number is 0,
// occlusion has been calculated with no visible root windows.
base::RunLoop().RunUntilIdle();
} while (GetNumVisibleRootWindows() != 0);
base::RunLoop run_loop3;
observer.set_expectation(Window::OcclusionState::VISIBLE);
observer.set_quit_closure(run_loop3.QuitClosure());
host()->window()->Show();
// This opens the window made iconic above.
OpenIcon(host()->GetAcceleratedWidget());
run_loop3.Run();
// Open a native window that occludes the visible app window.
base::RunLoop run_loop4;
observer.set_expectation(Window::OcclusionState::OCCLUDED);
observer.set_quit_closure(run_loop4.QuitClosure());
CreateNativeWindowWithBounds(gfx::Rect(0, 0, 100, 100));
run_loop4.Run();
EXPECT_FALSE(observer.is_expecting_call());
}
} // namespace aura
......@@ -856,6 +856,23 @@ void DesktopWindowTreeHostWin::HandleVisibilityChanged(bool visible) {
native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible);
}
void DesktopWindowTreeHostWin::HandleWindowMinimizedOrRestored(bool restored) {
// Ignore minimize/restore events that happen before widget initialization is
// done. If a window is created minimized, and then activated, restoring
// focus will fail because the root window is not visible, which is exposed by
// ExtensionWindowCreateTest.AcceptState.
if (!native_widget_delegate_->IsNativeWidgetInitialized())
return;
if (restored)
window()->Show();
else
window()->Hide();
if (compositor())
compositor()->SetVisible(restored);
}
void DesktopWindowTreeHostWin::HandleClientSizeChanged(
const gfx::Size& new_size) {
CheckForMonitorChange();
......
......@@ -196,6 +196,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
void HandleWorkAreaChanged() override;
void HandleVisibilityChanging(bool visible) override;
void HandleVisibilityChanged(bool visible) override;
void HandleWindowMinimizedOrRestored(bool restored) override;
void HandleClientSizeChanged(const gfx::Size& new_size) override;
void HandleFrameChanged() override;
void HandleNativeFocus(HWND last_focused_window) override;
......
......@@ -48,6 +48,9 @@ class VIEWS_EXPORT NativeWidgetDelegate {
// Returns true if the window can be activated.
virtual bool CanActivate() const = 0;
// Returns true if the native widget has been initialized.
virtual bool IsNativeWidgetInitialized() const = 0;
// Prevents the window from being rendered as deactivated. This state is
// reset automatically as soon as the window becomes activated again. There is
// no ability to control the state through this API as this leads to sync
......
......@@ -1038,6 +1038,10 @@ bool Widget::IsAlwaysRenderAsActive() const {
return always_render_as_active_;
}
bool Widget::IsNativeWidgetInitialized() const {
return native_widget_initialized_;
}
bool Widget::OnNativeWidgetActivationChanged(bool active) {
if (g_disable_activation_change_handling_)
return false;
......
......@@ -780,6 +780,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
bool IsModal() const override;
bool IsDialogBox() const override;
bool CanActivate() const override;
bool IsNativeWidgetInitialized() const override;
bool IsAlwaysRenderAsActive() const override;
void SetAlwaysRenderAsActive(bool always_render_as_active) override;
bool OnNativeWidgetActivationChanged(bool active) override;
......
......@@ -2473,6 +2473,13 @@ void HWNDMessageHandler::OnSysCommand(UINT notification_code,
if (!ref.get())
return;
if (IsTopLevelWindow(hwnd()) &&
((notification_code & sc_mask) == SC_MINIMIZE ||
(notification_code & sc_mask) == SC_RESTORE)) {
delegate_->HandleWindowMinimizedOrRestored(
(notification_code & sc_mask) == SC_RESTORE);
}
in_size_loop_ = false;
}
}
......
......@@ -175,6 +175,9 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate {
// Called when the window's visibility changed. |visible| holds the new state.
virtual void HandleVisibilityChanged(bool visible) = 0;
// Called when a top level window is minimized or restored.
virtual void HandleWindowMinimizedOrRestored(bool restored) = 0;
// Called when the window's client size changed. |new_size| holds the new
// size.
virtual void HandleClientSizeChanged(const gfx::Size& new_size) = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment