diff options
Diffstat (limited to '0009-ozone-wayland-window-state-change-must-be-synchronou.patch')
-rw-r--r-- | 0009-ozone-wayland-window-state-change-must-be-synchronou.patch | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/0009-ozone-wayland-window-state-change-must-be-synchronou.patch b/0009-ozone-wayland-window-state-change-must-be-synchronou.patch new file mode 100644 index 000000000000..1386e82e4e62 --- /dev/null +++ b/0009-ozone-wayland-window-state-change-must-be-synchronou.patch @@ -0,0 +1,665 @@ +From f70d9e1475b402cc70ff0b4539c26ff86ef1116c Mon Sep 17 00:00:00 2001 +From: Maksim Sisov <msisov@igalia.com> +Date: Tue, 7 Jan 2020 18:17:05 +0000 +Subject: [PATCH 9/9] ozone/wayland: window state change must be synchronous. + +This CL fixes two issues: + +1) After we changed to only create ShellSurfaces after Show calls, starting the +browser with maximized or fullscreen modes broke. That is, in Wayland, it +is required to assign a role to wl_surface and make higher level objects like +xdg_surface and xdg_toplevel that are used by desktop xdg shell. + +State manipulation can only be done through those higher level objects and +we must defer setting state to maximized/fullscreen until they are created. + +2) Also, this CL changes the intermidiate result of state changes: now, all of +them are considered synchronous and if Wayland decides to change the state +to something else, the state will be overriden and OnWindowStateChange will +be called. Otherwise, if it was the client, who changed the state, no +notification is sent. + +Test: StartMaximized, StartWithFullscreen, CompositorSideChanges +Bug: 1033525 + +Change-Id: I89728297d572d62db59f2d3b1628f2d735dbd4b0 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1965031 +Reviewed-by: Michael Spang <spang@chromium.org> +Commit-Queue: Maksim Sisov <msisov@igalia.com> +Cr-Commit-Position: refs/heads/master@{#728996} +--- + .../platform/wayland/host/wayland_surface.cc | 173 ++++++-------- + .../platform/wayland/host/wayland_surface.h | 17 +- + .../wayland/host/wayland_window_unittest.cc | 212 +++++++++++++++--- + 3 files changed, 249 insertions(+), 153 deletions(-) + +diff --git a/ui/ozone/platform/wayland/host/wayland_surface.cc b/ui/ozone/platform/wayland/host/wayland_surface.cc +index b800a526e64e..c90e499fc796 100644 +--- a/ui/ozone/platform/wayland/host/wayland_surface.cc ++++ b/ui/ozone/platform/wayland/host/wayland_surface.cc +@@ -19,8 +19,7 @@ namespace ui { + WaylandSurface::WaylandSurface(PlatformWindowDelegate* delegate, + WaylandConnection* connection) + : WaylandWindow(delegate, connection), +- state_(PlatformWindowState::kNormal), +- pending_state_(PlatformWindowState::kUnknown) { ++ state_(PlatformWindowState::kNormal) { + // Set a class property key, which allows |this| to be used for interactive + // events, e.g. move or resize. + SetWmMoveResizeHandler(this, AsWmMoveResizeHandler()); +@@ -47,6 +46,7 @@ bool WaylandSurface::CreateShellSurface() { + shell_surface_->SetAppId(app_id_); + shell_surface_->SetTitle(window_title_); + SetSizeConstraints(); ++ TriggerStateChanges(); + return true; + } + +@@ -117,7 +117,7 @@ void WaylandSurface::Hide() { + bool WaylandSurface::IsVisible() const { + // X and Windows return true if the window is minimized. For consistency, do + // the same. +- return !!shell_surface_ || IsMinimized(); ++ return !!shell_surface_ || state_ == PlatformWindowState::kMinimized; + } + + void WaylandSurface::SetTitle(const base::string16& title) { +@@ -133,74 +133,36 @@ void WaylandSurface::SetTitle(const base::string16& title) { + } + + void WaylandSurface::ToggleFullscreen() { +- DCHECK(shell_surface_); +- +- // There are some cases, when Chromium triggers a fullscreen state change +- // before the surface is activated. In such cases, Wayland may ignore state +- // changes and such flags as --kiosk or --start-fullscreen will be ignored. +- // To overcome this, set a pending state, and once the surface is activated, +- // trigger the change. +- if (!is_active_) { +- DCHECK(!IsFullscreen()); +- pending_state_ = PlatformWindowState::kFullScreen; +- return; +- } +- + // TODO(msisov, tonikitoo): add multiscreen support. As the documentation says +- // if shell_surface_set_fullscreen() is not provided with wl_output, it's up ++ // if xdg_toplevel_set_fullscreen() is not provided with wl_output, it's up + // to the compositor to choose which display will be used to map this surface. +- if (!IsFullscreen()) { +- // Fullscreen state changes have to be handled manually and then checked +- // against configuration events, which come from a compositor. The reason +- // of manually changing the |state_| is that the compositor answers about +- // state changes asynchronously, which leads to a wrong return value in +- // DesktopWindowTreeHostPlatform::IsFullscreen, for example, and media +- // files can never be set to fullscreen. +- state_ = PlatformWindowState::kFullScreen; +- shell_surface_->SetFullscreen(); ++ ++ // We must track the previous state to correctly say our state as long as it ++ // can be the maximized instead of normal one. ++ PlatformWindowState new_state = PlatformWindowState::kUnknown; ++ if (state_ == PlatformWindowState::kFullScreen) { ++ if (previous_state_ == PlatformWindowState::kMaximized) ++ new_state = previous_state_; ++ else ++ new_state = PlatformWindowState::kNormal; + } else { +- // Check the comment above. If it's not handled synchronously, media files +- // may not leave the fullscreen mode. +- state_ = PlatformWindowState::kUnknown; +- shell_surface_->UnSetFullscreen(); ++ new_state = PlatformWindowState::kFullScreen; + } + +- connection()->ScheduleFlush(); ++ SetWindowState(new_state); + } + + void WaylandSurface::Maximize() { +- DCHECK(shell_surface_); +- +- if (IsFullscreen()) +- ToggleFullscreen(); +- +- shell_surface_->SetMaximized(); +- connection()->ScheduleFlush(); ++ SetWindowState(PlatformWindowState::kMaximized); + } + + void WaylandSurface::Minimize() { +- DCHECK(shell_surface_); +- DCHECK(!is_minimizing_); +- // Wayland doesn't explicitly say if a window is minimized. Instead, it +- // notifies that the window is not activated. But there are many cases, when +- // the window is not minimized and deactivated. In order to properly record +- // the minimized state, mark this window as being minimized. And as soon as a +- // configuration event comes, check if the window has been deactivated and has +- // |is_minimizing_| set. +- is_minimizing_ = true; +- shell_surface_->SetMinimized(); +- connection()->ScheduleFlush(); ++ SetWindowState(PlatformWindowState::kMinimized); + } + + void WaylandSurface::Restore() { + DCHECK(shell_surface_); +- +- // Unfullscreen the window if it is fullscreen. +- if (IsFullscreen()) +- ToggleFullscreen(); +- +- shell_surface_->UnSetMaximized(); +- connection()->ScheduleFlush(); ++ SetWindowState(PlatformWindowState::kNormal); + } + + PlatformWindowState WaylandSurface::GetPlatformWindowState() const { +@@ -223,34 +185,21 @@ void WaylandSurface::HandleSurfaceConfigure(int32_t width, + bool is_maximized, + bool is_fullscreen, + bool is_activated) { +- // Propagate the window state information to the client. ++ // Store the old state to propagte state changes if Wayland decides to change ++ // the state to something else. + PlatformWindowState old_state = state_; +- +- // Ensure that manually handled state changes to fullscreen correspond to the +- // configuration events from a compositor. +- DCHECK_EQ(is_fullscreen, IsFullscreen()); +- +- // There are two cases, which must be handled for the minimized state. +- // The first one is the case, when the surface goes into the minimized state +- // (check comment in WaylandSurface::Minimize), and the second case is when +- // the surface still has been minimized, but another configuration event with +- // !is_activated comes. For this, check if the WaylandSurface has been +- // minimized before and !is_activated is sent. +- if ((is_minimizing_ || IsMinimized()) && !is_activated) { +- is_minimizing_ = false; ++ if (state_ == PlatformWindowState::kMinimized && !is_activated) { + state_ = PlatformWindowState::kMinimized; + } else if (is_fullscreen) { +- // To ensure the |delegate()| is notified about state changes to fullscreen, +- // assume the old_state is UNKNOWN (check comment in ToggleFullscreen). +- old_state = PlatformWindowState::kUnknown; +- DCHECK(state_ == PlatformWindowState::kFullScreen); ++ state_ = PlatformWindowState::kFullScreen; + } else if (is_maximized) { + state_ = PlatformWindowState::kMaximized; + } else { + state_ = PlatformWindowState::kNormal; + } ++ + const bool state_changed = old_state != state_; +- const bool is_normal = !IsFullscreen() && !IsMaximized(); ++ const bool is_normal = state_ == PlatformWindowState::kNormal; + + // Update state before notifying delegate. + const bool did_active_change = is_active_ != is_activated; +@@ -281,28 +230,17 @@ void WaylandSurface::HandleSurfaceConfigure(int32_t width, + 1.0 / buffer_scale())); + } + +- if (state_changed) { +- // The |restored_bounds_| are used when the window gets back to normal +- // state after it went maximized or fullscreen. So we reset these if the +- // window has just become normal and store the current bounds if it is +- // either going out of normal state or simply changes the state and we don't +- // have any meaningful value stored. +- if (is_normal) { +- SetRestoredBoundsInPixels({}); +- } else if (old_state == PlatformWindowState::kNormal || +- GetRestoredBoundsInPixels().IsEmpty()) { +- SetRestoredBoundsInPixels(GetBounds()); +- } ++ // Store the restored bounds of current state differs from the normal state. ++ // It can be client or compositor side change from normal to something else. ++ // Thus, we must store previous bounds to restore later. ++ SetOrResetRestoredBounds(); ++ ApplyPendingBounds(); + ++ if (state_changed) + delegate()->OnWindowStateChanged(state_); +- } +- +- ApplyPendingBounds(); + + if (did_active_change) + delegate()->OnActivationChanged(is_active_); +- +- MaybeTriggerPendingStateChange(); + } + + void WaylandSurface::OnDragEnter(const gfx::PointF& point, +@@ -348,24 +286,34 @@ bool WaylandSurface::OnInitialize(PlatformWindowInitProperties properties) { + return true; + } + +-bool WaylandSurface::IsMinimized() const { +- return state_ == PlatformWindowState::kMinimized; +-} ++void WaylandSurface::TriggerStateChanges() { ++ if (!shell_surface_) ++ return; + +-bool WaylandSurface::IsMaximized() const { +- return state_ == PlatformWindowState::kMaximized; +-} ++ if (state_ == PlatformWindowState::kFullScreen) ++ shell_surface_->SetFullscreen(); ++ else ++ shell_surface_->UnSetFullscreen(); ++ ++ // Call UnSetMaximized only if current state is normal. Otherwise, if the ++ // current state is fullscreen and the previous is maximized, calling ++ // UnSetMaximized may result in wrong restored window position that clients ++ // are not allowed to know about. ++ if (state_ == PlatformWindowState::kMaximized) ++ shell_surface_->SetMaximized(); ++ else if (state_ == PlatformWindowState::kNormal) ++ shell_surface_->UnSetMaximized(); ++ ++ if (state_ == PlatformWindowState::kMinimized) ++ shell_surface_->SetMinimized(); + +-bool WaylandSurface::IsFullscreen() const { +- return state_ == PlatformWindowState::kFullScreen; ++ connection()->ScheduleFlush(); + } + +-void WaylandSurface::MaybeTriggerPendingStateChange() { +- if (pending_state_ == PlatformWindowState::kUnknown || !is_active_) +- return; +- DCHECK_EQ(pending_state_, PlatformWindowState::kFullScreen); +- pending_state_ = PlatformWindowState::kUnknown; +- ToggleFullscreen(); ++void WaylandSurface::SetWindowState(PlatformWindowState state) { ++ previous_state_ = state_; ++ state_ = state; ++ TriggerStateChanges(); + } + + WmMoveResizeHandler* WaylandSurface::AsWmMoveResizeHandler() { +@@ -381,4 +329,17 @@ void WaylandSurface::SetSizeConstraints() { + connection()->ScheduleFlush(); + } + ++void WaylandSurface::SetOrResetRestoredBounds() { ++ // The |restored_bounds_| are used when the window gets back to normal ++ // state after it went maximized or fullscreen. So we reset these if the ++ // window has just become normal and store the current bounds if it is ++ // either going out of normal state or simply changes the state and we don't ++ // have any meaningful value stored. ++ if (GetPlatformWindowState() == PlatformWindowState::kNormal) { ++ SetRestoredBoundsInPixels({}); ++ } else if (GetRestoredBoundsInPixels().IsEmpty()) { ++ SetRestoredBoundsInPixels(GetBounds()); ++ } ++} ++ + } // namespace ui +diff --git a/ui/ozone/platform/wayland/host/wayland_surface.h b/ui/ozone/platform/wayland/host/wayland_surface.h +index 677eaca6ef56..ceda32d24a7c 100644 +--- a/ui/ozone/platform/wayland/host/wayland_surface.h ++++ b/ui/ozone/platform/wayland/host/wayland_surface.h +@@ -69,11 +69,8 @@ class WaylandSurface : public WaylandWindow, + void OnDragSessionClose(uint32_t dnd_action) override; + bool OnInitialize(PlatformWindowInitProperties properties) override; + +- bool IsMinimized() const; +- bool IsMaximized() const; +- bool IsFullscreen() const; +- +- void MaybeTriggerPendingStateChange(); ++ void TriggerStateChanges(); ++ void SetWindowState(PlatformWindowState state); + + // Creates a surface window, which is visible as a main window. + bool CreateShellSurface(); +@@ -83,6 +80,8 @@ class WaylandSurface : public WaylandWindow, + // Propagates the |min_size_| and |max_size_| to the ShellSurface. + void SetSizeConstraints(); + ++ void SetOrResetRestoredBounds(); ++ + // Wrappers around shell surface. + std::unique_ptr<ShellSurfaceWrapper> shell_surface_; + +@@ -100,14 +99,12 @@ class WaylandSurface : public WaylandWindow, + // handler that receives DIP from Wayland. + gfx::Rect pending_bounds_dip_; + +- // Stores current states of the window. ++ // Contains the current state of the window. + PlatformWindowState state_; +- // Stores a pending state of the window, which is used before the surface is +- // activated. +- PlatformWindowState pending_state_; ++ // Contains the previous state of the window. ++ PlatformWindowState previous_state_; + + bool is_active_ = false; +- bool is_minimizing_ = false; + + // Id of the chromium app passed through + // PlatformWindowInitProperties::wm_class_class. This is used by Wayland +diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +index f56b96690eea..c72d27edff66 100644 +--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc ++++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +@@ -233,8 +233,7 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { + kMaximizedBounds.height())); + EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); + EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds)); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kMaximized))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->Maximize(); + SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 1, + active_maximized.get()); +@@ -262,8 +261,7 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) { + + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(), + kNormalBounds.height())); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kNormal))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnActivationChanged(_)).Times(0); + EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); + EXPECT_CALL(*GetXdgToplevel(), UnsetMaximized()); +@@ -283,19 +281,21 @@ TEST_P(WaylandWindowTest, Minimize) { + Sync(); + + EXPECT_CALL(*GetXdgToplevel(), SetMinimized()); +- // Wayland compositor doesn't notify clients about minimized state, but rather +- // if a window is not activated. Thus, a WaylandWindow marks itself as being +- // minimized and as soon as a configuration event with not activated state +- // comes, its state is changed to minimized. This EXPECT_CALL ensures this +- // behaviour. +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kMinimized))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->Minimize(); ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized); ++ + // Reinitialize wl_array, which removes previous old states. + states = ScopedWlArray(); + SendConfigureEvent(0, 0, 2, states.get()); + Sync(); + ++ // Wayland compositor doesn't notify clients about minimized state, but rather ++ // if a window is not activated. Thus, a WaylandSurface marks itself as being ++ // minimized and and sets state to minimized. Thus, the state mustn't change ++ // after the configuration event is sent. ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized); ++ + // Send one additional empty configuration event (which means the surface is + // not maximized, fullscreen or activated) to ensure, WaylandWindow stays in + // the same minimized state and doesn't notify its delegate. +@@ -319,8 +319,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { + AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); + + EXPECT_CALL(*GetXdgToplevel(), SetFullscreen()); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->ToggleFullscreen(); + // Make sure than WaylandWindow manually handles fullscreen states. Check the + // comment in the WaylandWindow::ToggleFullscreen. +@@ -330,51 +329,181 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { + Sync(); + + EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen()); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kNormal))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->Restore(); +- EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kUnknown); ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); + // Reinitialize wl_array, which removes previous old states. + states = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(0, 0, 3, states.get()); + Sync(); ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); + } + + TEST_P(WaylandWindowTest, StartWithFullscreen) { ++ MockPlatformWindowDelegate delegate; ++ PlatformWindowInitProperties properties; ++ properties.bounds = gfx::Rect(0, 0, 100, 100); ++ properties.type = PlatformWindowType::kWindow; ++ // We need to create a window avoid calling Show() on it as it is what upper ++ // views layer does - when Widget initialize DesktopWindowTreeHost, the Show() ++ // is called later down the road, but Maximize may be called earlier. We ++ // cannot process them and set a pending state instead, because ShellSurface ++ // is not created by that moment. ++ auto window = WaylandWindow::Create(&delegate, connection_.get(), ++ std::move(properties)); ++ ++ Sync(); ++ ++ // Make sure the window is initialized to normal state from the beginning. ++ EXPECT_EQ(PlatformWindowState::kNormal, window->GetPlatformWindowState()); ++ ++ // The state must not be changed to the fullscreen before the surface is ++ // activated. ++ auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); ++ EXPECT_FALSE(mock_surface->xdg_surface()); ++ EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0); ++ window->ToggleFullscreen(); ++ // The state of the window must already be fullscreen one. ++ EXPECT_EQ(window->GetPlatformWindowState(), PlatformWindowState::kFullScreen); ++ ++ Sync(); ++ ++ // We mustn't receive any state changes if that does not differ from the last ++ // state. ++ EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0); ++ ++ // Activate the surface. ++ ScopedWlArray states = InitializeWlArrayWithActivatedState(); ++ AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); ++ SendConfigureEvent(0, 0, 1, states.get()); ++ ++ Sync(); ++ ++ // It must be still the same state. ++ EXPECT_EQ(window->GetPlatformWindowState(), PlatformWindowState::kFullScreen); ++} ++ ++TEST_P(WaylandWindowTest, StartMaximized) { ++ MockPlatformWindowDelegate delegate; ++ PlatformWindowInitProperties properties; ++ properties.bounds = gfx::Rect(0, 0, 100, 100); ++ properties.type = PlatformWindowType::kWindow; ++ // We need to create a window avoid calling Show() on it as it is what upper ++ // views layer does - when Widget initialize DesktopWindowTreeHost, the Show() ++ // is called later down the road, but Maximize may be called earlier. We ++ // cannot process them and set a pending state instead, because ShellSurface ++ // is not created by that moment. ++ auto window = WaylandWindow::Create(&delegate, connection_.get(), ++ std::move(properties)); ++ ++ Sync(); ++ + // Make sure the window is initialized to normal state from the beginning. + EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); + + // The state must not be changed to the fullscreen before the surface is + // activated. +- EXPECT_CALL(*GetXdgToplevel(), SetFullscreen()).Times(0); ++ auto* mock_surface = server_.GetObject<wl::MockSurface>(window->GetWidget()); ++ EXPECT_FALSE(mock_surface->xdg_surface()); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); +- window_->ToggleFullscreen(); +- // The state of the window must still be a normal one. +- EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); ++ ++ window_->Maximize(); ++ // The state of the window must already be fullscreen one. ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized); + + Sync(); + +- // Once the surface will be activated, the window will automatically trigger +- // the state change. +- EXPECT_CALL(*GetXdgToplevel(), SetFullscreen()); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))); ++ // Once the surface will be activated, the window state mustn't be changed ++ // and retain the same. ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized); + + // Activate the surface. + ScopedWlArray states = InitializeWlArrayWithActivatedState(); ++ AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); + SendConfigureEvent(0, 0, 1, states.get()); + + Sync(); + +- // The wayland window manually handles the fullscreen state changes, and it +- // must change to a fullscreen before the state change is confirmed by the +- // wayland. See comment in the WaylandWindow::ToggleFullscreen. +- EXPECT_EQ(window_->GetPlatformWindowState(), +- PlatformWindowState::kFullScreen); ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized); ++} + +- AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); ++TEST_P(WaylandWindowTest, CompositorSideStateChanges) { ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal); ++ auto normal_bounds = window_->GetBounds(); ++ ++ ScopedWlArray states = InitializeWlArrayWithActivatedState(); ++ AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); ++ SendConfigureEvent(2000, 2000, 1, states.get()); ++ ++ EXPECT_CALL(delegate_, ++ OnWindowStateChanged(Eq(PlatformWindowState::kMaximized))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2000, 2000)); ++ ++ Sync(); ++ ++ EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized); ++ ++ // Unmaximize ++ states = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(0, 0, 2, states.get()); + ++ EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(), ++ normal_bounds.height())); ++ ++ // Now, set to fullscreen. ++ AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); ++ SendConfigureEvent(2005, 2005, 3, states.get()); ++ EXPECT_CALL(delegate_, ++ OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2005, 2005)); ++ ++ Sync(); ++ ++ // Unfullscreen ++ states = InitializeWlArrayWithActivatedState(); ++ SendConfigureEvent(0, 0, 4, states.get()); ++ ++ EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(), ++ normal_bounds.height())); ++ ++ Sync(); ++ ++ // Now, maximize, fullscreen and restore. ++ states = InitializeWlArrayWithActivatedState(); ++ AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get()); ++ SendConfigureEvent(2000, 2000, 1, states.get()); ++ ++ EXPECT_CALL(delegate_, ++ OnWindowStateChanged(Eq(PlatformWindowState::kMaximized))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2000, 2000)); ++ ++ Sync(); ++ ++ AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get()); ++ SendConfigureEvent(2005, 2005, 1, states.get()); ++ ++ EXPECT_CALL(delegate_, ++ OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2005, 2005)); ++ ++ // Restore ++ states = InitializeWlArrayWithActivatedState(); ++ SendConfigureEvent(0, 0, 4, states.get()); ++ ++ EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal))) ++ .Times(1); ++ EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(), ++ normal_bounds.height())); ++ + Sync(); + } + +@@ -395,25 +524,33 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { + kMaximizedBounds.height())); + EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); + EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds)); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kMaximized))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->Maximize(); ++ // State changes are synchronous. ++ EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState()); + SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 2, + active_maximized.get()); + Sync(); ++ // Verify that the state has not been changed. ++ EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState()); + VerifyAndClearExpectations(); + + EXPECT_CALL(*GetXdgToplevel(), SetFullscreen()); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(), + kMaximizedBounds.height())); + EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->ToggleFullscreen(); ++ // State changes are synchronous. ++ EXPECT_EQ(PlatformWindowState::kFullScreen, ++ window_->GetPlatformWindowState()); + AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, active_maximized.get()); + SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 3, + active_maximized.get()); + Sync(); ++ // Verify that the state has not been changed. ++ EXPECT_EQ(PlatformWindowState::kFullScreen, ++ window_->GetPlatformWindowState()); + VerifyAndClearExpectations(); + + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(), +@@ -421,13 +558,14 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { + EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen()); + EXPECT_CALL(*GetXdgToplevel(), UnsetMaximized()); + EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds)); +- EXPECT_CALL(delegate_, +- OnWindowStateChanged(Eq(PlatformWindowState::kNormal))); ++ EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->Restore(); ++ EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); + // Reinitialize wl_array, which removes previous old states. + auto active = InitializeWlArrayWithActivatedState(); + SendConfigureEvent(0, 0, 4, active.get()); + Sync(); ++ EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState()); + } + + TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) { +-- +2.24.1 + |