Commit ee2c93bd authored by Tom Anderson's avatar Tom Anderson Committed by Commit Bot

Confirm-to-quit: Close bubble when browser loses focus

Sometimes, another app can grab the focus while the confirm-to-quit bubble is
showing. When this happens, Chrome won't get the key release event and the
bubble will stay open forever.  The only way to close it would be to press
Ctrl+Shift+Q again, but this would close Chrome.

This CL closes the confirm-to-quit bubble and resets the state of the controller
when this happens.

BUG=243164
R=sky

Change-Id: I1800501be3cd4896876347ce2fb511a08e90eba7
Reviewed-on: https://chromium-review.googlesource.com/1069487Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561377}
parent 8d3410ac
...@@ -52,9 +52,12 @@ ConfirmQuitBubbleController::ConfirmQuitBubbleController( ...@@ -52,9 +52,12 @@ ConfirmQuitBubbleController::ConfirmQuitBubbleController(
browser_hide_animation_(std::move(animation)) { browser_hide_animation_(std::move(animation)) {
browser_hide_animation_->SetSlideDuration( browser_hide_animation_->SetSlideDuration(
kWindowFadeOutDuration.InMilliseconds()); kWindowFadeOutDuration.InMilliseconds());
BrowserList::AddObserver(this);
} }
ConfirmQuitBubbleController::~ConfirmQuitBubbleController() {} ConfirmQuitBubbleController::~ConfirmQuitBubbleController() {
BrowserList::RemoveObserver(this);
}
bool ConfirmQuitBubbleController::HandleKeyboardEvent( bool ConfirmQuitBubbleController::HandleKeyboardEvent(
const ui::Accelerator& accelerator) { const ui::Accelerator& accelerator) {
...@@ -66,6 +69,7 @@ bool ConfirmQuitBubbleController::HandleKeyboardEvent( ...@@ -66,6 +69,7 @@ bool ConfirmQuitBubbleController::HandleKeyboardEvent(
!accelerator.IsRepeat()) { !accelerator.IsRepeat()) {
if (state_ == State::kWaiting) { if (state_ == State::kWaiting) {
state_ = State::kPressed; state_ = State::kPressed;
browser_ = BrowserList::GetInstance()->GetLastActive();
view_->Show(); view_->Show();
hide_timer_->Start(FROM_HERE, kShowDuration, this, hide_timer_->Start(FROM_HERE, kShowDuration, this,
&ConfirmQuitBubbleController::OnTimerElapsed); &ConfirmQuitBubbleController::OnTimerElapsed);
...@@ -101,6 +105,17 @@ void ConfirmQuitBubbleController::AnimationEnded( ...@@ -101,6 +105,17 @@ void ConfirmQuitBubbleController::AnimationEnded(
AnimationProgressed(animation); AnimationProgressed(animation);
} }
void ConfirmQuitBubbleController::OnBrowserNoLongerActive(Browser* browser) {
if (browser != browser_ || state_ == State::kWaiting ||
state_ == State::kQuitting) {
return;
}
state_ = State::kWaiting;
view_->Hide();
hide_timer_->Stop();
browser_hide_animation_->Hide();
}
void ConfirmQuitBubbleController::OnTimerElapsed() { void ConfirmQuitBubbleController::OnTimerElapsed() {
if (state_ == State::kPressed) { if (state_ == State::kPressed) {
// The accelerator was held down the entire time the bubble was showing. // The accelerator was held down the entire time the bubble was showing.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/animation_delegate.h"
class ConfirmQuitBubbleBase; class ConfirmQuitBubbleBase;
...@@ -28,7 +29,8 @@ class Accelerator; ...@@ -28,7 +29,8 @@ class Accelerator;
// Manages showing and hiding the confirm-to-quit bubble. Requests Chrome to be // Manages showing and hiding the confirm-to-quit bubble. Requests Chrome to be
// closed if the quit accelerator is held down or pressed twice in succession. // closed if the quit accelerator is held down or pressed twice in succession.
class ConfirmQuitBubbleController : public gfx::AnimationDelegate { class ConfirmQuitBubbleController : public gfx::AnimationDelegate,
public BrowserListObserver {
public: public:
static ConfirmQuitBubbleController* GetInstance(); static ConfirmQuitBubbleController* GetInstance();
...@@ -71,6 +73,9 @@ class ConfirmQuitBubbleController : public gfx::AnimationDelegate { ...@@ -71,6 +73,9 @@ class ConfirmQuitBubbleController : public gfx::AnimationDelegate {
void AnimationProgressed(const gfx::Animation* animation) override; void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override; void AnimationEnded(const gfx::Animation* animation) override;
// BrowserListObserver:
void OnBrowserNoLongerActive(Browser* browser) override;
void OnTimerElapsed(); void OnTimerElapsed();
void ConfirmQuit(); void ConfirmQuit();
...@@ -83,6 +88,9 @@ class ConfirmQuitBubbleController : public gfx::AnimationDelegate { ...@@ -83,6 +88,9 @@ class ConfirmQuitBubbleController : public gfx::AnimationDelegate {
State state_; State state_;
// The last active browser when the accelerator was pressed.
Browser* browser_ = nullptr;
std::unique_ptr<base::Timer> hide_timer_; std::unique_ptr<base::Timer> hide_timer_;
std::unique_ptr<gfx::SlideAnimation> const browser_hide_animation_; std::unique_ptr<gfx::SlideAnimation> const browser_hide_animation_;
......
...@@ -86,6 +86,8 @@ class ConfirmQuitBubbleControllerTest : public testing::Test { ...@@ -86,6 +86,8 @@ class ConfirmQuitBubbleControllerTest : public testing::Test {
void ReleaseOtherAccelerator() { SendAccelerator(false, false, false); } void ReleaseOtherAccelerator() { SendAccelerator(false, false, false); }
void DeactivateBrowser() { controller_->OnBrowserNoLongerActive(nullptr); }
std::unique_ptr<ConfirmQuitBubbleController> controller_; std::unique_ptr<ConfirmQuitBubbleController> controller_;
// Owned by |controller_|. // Owned by |controller_|.
...@@ -151,3 +153,31 @@ TEST_F(ConfirmQuitBubbleControllerTest, OtherKeyPress) { ...@@ -151,3 +153,31 @@ TEST_F(ConfirmQuitBubbleControllerTest, OtherKeyPress) {
ReleaseQuitAccelerator(); ReleaseQuitAccelerator();
EXPECT_TRUE(quit_called_); EXPECT_TRUE(quit_called_);
} }
// The controller state should be reset when the browser loses focus.
TEST_F(ConfirmQuitBubbleControllerTest, BrowserLosesFocus) {
// Press but don't release the accelerator.
PressQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
DeactivateBrowser();
EXPECT_FALSE(timer_->IsRunning());
EXPECT_FALSE(quit_called_);
ReleaseQuitAccelerator();
// Press and release the accelerator.
PressQuitAccelerator();
ReleaseQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
DeactivateBrowser();
EXPECT_FALSE(timer_->IsRunning());
EXPECT_FALSE(quit_called_);
// Press and hold the accelerator.
PressQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
timer_->Fire();
EXPECT_FALSE(timer_->IsRunning());
DeactivateBrowser();
ReleaseQuitAccelerator();
EXPECT_FALSE(quit_called_);
}
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