Commit 4a018235 authored by James Cook's avatar James Cook Committed by Commit Bot

Speculative fix for crash in ash::Shelf::SetAlignment()

When disconnecting an external monitor the display manager shuts down
the associated RootWindowController, but defers deletion of the
RootWindowController via a posted task. This means an ash::Shelf
can exist without a ShelfWidget.

A similar code path exists for entering tablet mode, which turns on
display mirroring, and shuts down the RootWindowController for the
external display.

If there's a pending task that changes session state (for example,
the user's sign-in completes), then SessionController observers can
fire and attempt to update the alignment of the shelf.

Add null checks to ash::Shelf for shelf_widget_. I think we used to
have these many years ago, and I think I removed them in 2016 when
when cleaning up ash::Shelf. Sadly, they are still needed.

Bug: 937495, 738011
Test: ash_unittests
Change-Id: I794c610ba1f56483dae49f49e6d955ccf15ce10d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1542572Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#645321}
parent 45fd5d5f
...@@ -548,6 +548,7 @@ void WindowTreeHostManager::DeleteHost(AshWindowTreeHost* host_to_delete) { ...@@ -548,6 +548,7 @@ void WindowTreeHostManager::DeleteHost(AshWindowTreeHost* host_to_delete) {
controller->Shutdown(); controller->Shutdown();
if (primary_tree_host_for_replace_ == host_to_delete) if (primary_tree_host_for_replace_ == host_to_delete)
primary_tree_host_for_replace_ = nullptr; primary_tree_host_for_replace_ = nullptr;
// NOTE: ShelfWidget is gone, but Shelf still exists until this task runs.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, controller); base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, controller);
} }
......
...@@ -143,13 +143,12 @@ bool Shelf::IsVisible() const { ...@@ -143,13 +143,12 @@ bool Shelf::IsVisible() const {
} }
aura::Window* Shelf::GetWindow() { aura::Window* Shelf::GetWindow() {
return shelf_widget_->GetNativeWindow(); return shelf_widget_ ? shelf_widget_->GetNativeWindow() : nullptr;
} }
void Shelf::SetAlignment(ShelfAlignment alignment) { void Shelf::SetAlignment(ShelfAlignment alignment) {
// Checks added for http://crbug.com/738011. if (!shelf_widget_)
CHECK(shelf_widget_); return;
CHECK(shelf_layout_manager_);
if (alignment_ == alignment) if (alignment_ == alignment)
return; return;
...@@ -218,7 +217,8 @@ void Shelf::UpdateAutoHideState() { ...@@ -218,7 +217,8 @@ void Shelf::UpdateAutoHideState() {
} }
ShelfBackgroundType Shelf::GetBackgroundType() const { ShelfBackgroundType Shelf::GetBackgroundType() const {
return shelf_widget_->GetBackgroundType(); return shelf_widget_ ? shelf_widget_->GetBackgroundType()
: SHELF_BACKGROUND_DEFAULT;
} }
void Shelf::UpdateVisibilityState() { void Shelf::UpdateVisibilityState() {
...@@ -280,7 +280,7 @@ void Shelf::NotifyShelfIconPositionsChanged() { ...@@ -280,7 +280,7 @@ void Shelf::NotifyShelfIconPositionsChanged() {
} }
StatusAreaWidget* Shelf::GetStatusAreaWidget() const { StatusAreaWidget* Shelf::GetStatusAreaWidget() const {
return shelf_widget_->status_area_widget(); return shelf_widget_ ? shelf_widget_->status_area_widget() : nullptr;
} }
TrayBackgroundView* Shelf::GetSystemTrayAnchorView() const { TrayBackgroundView* Shelf::GetSystemTrayAnchorView() const {
......
...@@ -191,6 +191,8 @@ class ASH_EXPORT Shelf : public ShelfLayoutManagerObserver { ...@@ -191,6 +191,8 @@ class ASH_EXPORT Shelf : public ShelfLayoutManagerObserver {
// ShelfWidget and lifetimes are managed by the container windows themselves. // ShelfWidget and lifetimes are managed by the container windows themselves.
ShelfLayoutManager* shelf_layout_manager_ = nullptr; ShelfLayoutManager* shelf_layout_manager_ = nullptr;
// Null during display teardown, see WindowTreeHostManager::DeleteHost() and
// RootWindowController::CloseAllChildWindows().
std::unique_ptr<ShelfWidget> shelf_widget_; std::unique_ptr<ShelfWidget> shelf_widget_;
// These initial values hide the shelf until user preferences are available. // These initial values hide the shelf until user preferences are available.
......
...@@ -36,7 +36,7 @@ class ASH_EXPORT ShelfLockingManager : public SessionObserver, ...@@ -36,7 +36,7 @@ class ASH_EXPORT ShelfLockingManager : public SessionObserver,
// Update the shelf state for session and screen lock changes. // Update the shelf state for session and screen lock changes.
void UpdateLockedState(); void UpdateLockedState();
Shelf* shelf_; Shelf* const shelf_;
bool session_locked_ = false; bool session_locked_ = false;
bool screen_locked_ = false; bool screen_locked_ = false;
ShelfAlignment stored_alignment_; ShelfAlignment stored_alignment_;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "ash/public/cpp/shelf_model.h" #include "ash/public/cpp/shelf_model.h"
#include "ash/root_window_controller.h" #include "ash/root_window_controller.h"
#include "ash/session/session_controller.h"
#include "ash/session/test_session_controller_client.h" #include "ash/session/test_session_controller_client.h"
#include "ash/shelf/overflow_button.h" #include "ash/shelf/overflow_button.h"
#include "ash/shelf/shelf.h" #include "ash/shelf/shelf.h"
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/session_manager/session_manager_types.h" #include "components/session_manager/session_manager_types.h"
#include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/associated_binding.h"
...@@ -145,5 +147,31 @@ TEST_F(ShelfTest, ShelfHiddenOnScreenOnSecondaryDisplay) { ...@@ -145,5 +147,31 @@ TEST_F(ShelfTest, ShelfHiddenOnScreenOnSecondaryDisplay) {
} }
} }
using NoSessionShelfTest = NoSessionAshTestBase;
// Regression test for crash in Shelf::SetAlignment(). https://crbug.com/937495
TEST_F(NoSessionShelfTest, SetAlignmentDuringDisplayDisconnect) {
UpdateDisplay("1024x768,800x600");
base::RunLoop().RunUntilIdle();
// The task indirectly triggers Shelf::SetAlignment() via a SessionObserver.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestSessionControllerClient* session) {
session->SetSessionState(session_manager::SessionState::ACTIVE);
},
GetSessionControllerClient()));
// Remove the secondary display.
UpdateDisplay("1280x1024");
// The session activation task runs before the RootWindowController and the
// Shelf are deleted.
base::RunLoop().RunUntilIdle();
// No crash.
}
} // namespace } // namespace
} // namespace ash } // namespace ash
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