aura: Fix crash when the aura window hosting a tooltip is destroyed. We fix

this by listening to such events.

BUG=140811
TEST=added new test


Review URL: https://chromiumcodereview.appspot.com/10832194

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150488 0039d316-1c4b-4281-b951-d872f2087c98
parent 18b30d5e
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "ui/views/border.h" #include "ui/views/border.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
namespace { namespace {
...@@ -81,7 +82,6 @@ views::Widget* CreateTooltip() { ...@@ -81,7 +82,6 @@ views::Widget* CreateTooltip() {
params.type = views::Widget::InitParams::TYPE_TOOLTIP; params.type = views::Widget::InitParams::TYPE_TOOLTIP;
params.keep_on_top = true; params.keep_on_top = true;
params.accept_events = false; params.accept_events = false;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget->Init(params); widget->Init(params);
return widget; return widget;
} }
...@@ -92,9 +92,9 @@ namespace ash { ...@@ -92,9 +92,9 @@ namespace ash {
namespace internal { namespace internal {
// Displays a widget with tooltip using a views::Label. // Displays a widget with tooltip using a views::Label.
class TooltipController::Tooltip { class TooltipController::Tooltip : public views::WidgetObserver {
public: public:
Tooltip() { Tooltip() : widget_(NULL) {
label_.set_background( label_.set_background(
views::Background::CreateSolidBackground(kTooltipBackground)); views::Background::CreateSolidBackground(kTooltipBackground));
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAuraNoShadows)) { if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAuraNoShadows)) {
...@@ -103,12 +103,13 @@ class TooltipController::Tooltip { ...@@ -103,12 +103,13 @@ class TooltipController::Tooltip {
kTooltipBorder)); kTooltipBorder));
} }
label_.set_owned_by_client(); label_.set_owned_by_client();
widget_.reset(CreateTooltip());
widget_->SetContentsView(&label_);
} }
~Tooltip() { ~Tooltip() {
widget_->Close(); if (widget_) {
widget_->RemoveObserver(this);
widget_->Close();
}
} }
// Updates the text on the tooltip and resizes to fit. // Updates the text on the tooltip and resizes to fit.
...@@ -131,21 +132,28 @@ class TooltipController::Tooltip { ...@@ -131,21 +132,28 @@ class TooltipController::Tooltip {
// Shows the tooltip. // Shows the tooltip.
void Show() { void Show() {
widget_->Show(); GetWidget()->Show();
} }
// Hides the tooltip. // Hides the tooltip.
void Hide() { void Hide() {
widget_->Hide(); if (widget_)
widget_->Hide();
} }
bool IsVisible() { bool IsVisible() {
return widget_->IsVisible(); return widget_? widget_->IsVisible() : false;
}
// Overriden from views::WidgetObserver.
virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE {
DCHECK_EQ(widget_, widget);
widget_ = NULL;
} }
private: private:
views::Label label_; views::Label label_;
scoped_ptr<views::Widget> widget_; views::Widget* widget_;
// Adjusts the bounds given by the arguments to fit inside the desktop // Adjusts the bounds given by the arguments to fit inside the desktop
// and applies the adjusted bounds to the label_. // and applies the adjusted bounds to the label_.
...@@ -171,9 +179,17 @@ class TooltipController::Tooltip { ...@@ -171,9 +179,17 @@ class TooltipController::Tooltip {
if (tooltip_rect.bottom() > display_bounds.bottom()) if (tooltip_rect.bottom() > display_bounds.bottom())
tooltip_rect.set_y(mouse_pos.y() - tooltip_height); tooltip_rect.set_y(mouse_pos.y() - tooltip_height);
widget_->SetBounds(tooltip_rect.AdjustToFit(display_bounds)); GetWidget()->SetBounds(tooltip_rect.AdjustToFit(display_bounds));
} }
views::Widget* GetWidget() {
if (!widget_) {
widget_ = CreateTooltip();
widget_->SetContentsView(&label_);
widget_->AddObserver(this);
}
return widget_;
}
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "ash/display/display_controller.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "ash/tooltips/tooltip_controller.h" #include "ash/tooltips/tooltip_controller.h"
...@@ -43,19 +44,24 @@ class TooltipTestView : public views::View { ...@@ -43,19 +44,24 @@ class TooltipTestView : public views::View {
DISALLOW_COPY_AND_ASSIGN(TooltipTestView); DISALLOW_COPY_AND_ASSIGN(TooltipTestView);
}; };
views::Widget* CreateNewWidget() { views::Widget* CreateNewWidgetWithBounds(const gfx::Rect& bounds) {
views::Widget* widget = new views::Widget; views::Widget* widget = new views::Widget;
views::Widget::InitParams params; views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
params.accept_events = true; params.accept_events = true;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.parent = Shell::GetPrimaryRootWindow(); params.parent_widget = NULL;
params.child = true; params.child = true;
params.bounds = bounds;
widget->Init(params); widget->Init(params);
widget->Show(); widget->Show();
return widget; return widget;
} }
views::Widget* CreateNewWidget() {
return CreateNewWidgetWithBounds(gfx::Rect());
}
void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) { void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) {
if (!widget->GetContentsView()) { if (!widget->GetContentsView()) {
views::View* contents_view = new views::View; views::View* contents_view = new views::View;
...@@ -68,7 +74,8 @@ void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) { ...@@ -68,7 +74,8 @@ void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) {
gfx::Rect contents_view_bounds = contents_view->bounds(); gfx::Rect contents_view_bounds = contents_view->bounds();
contents_view_bounds = contents_view_bounds.Union(view->bounds()); contents_view_bounds = contents_view_bounds.Union(view->bounds());
contents_view->SetBoundsRect(contents_view_bounds); contents_view->SetBoundsRect(contents_view_bounds);
widget->SetBounds(contents_view_bounds); widget->SetBounds(gfx::Rect(widget->GetWindowBoundsInScreen().origin(),
contents_view_bounds.size()));
} }
ash::internal::TooltipController* GetController() { ash::internal::TooltipController* GetController() {
...@@ -471,5 +478,45 @@ TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { ...@@ -471,5 +478,45 @@ TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) {
EXPECT_EQ(window, GetTooltipWindow()); EXPECT_EQ(window, GetTooltipWindow());
} }
TEST_F(TooltipControllerTest, TooltipsOnMultiDisplayShouldNotCrash) {
internal::DisplayController::SetExtendedDesktopEnabled(true);
UpdateDisplay("1000x600,600x400");
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
scoped_ptr<views::Widget> widget1(CreateNewWidgetWithBounds(
gfx::Rect(10, 10, 100, 100)));
TooltipTestView* view1 = new TooltipTestView;
AddViewToWidgetAndResize(widget1.get(), view1);
view1->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1"));
EXPECT_EQ(widget1->GetNativeView()->GetRootWindow(), root_windows[0]);
scoped_ptr<views::Widget> widget2(CreateNewWidgetWithBounds(
gfx::Rect(1200, 10, 100, 100)));
TooltipTestView* view2 = new TooltipTestView;
AddViewToWidgetAndResize(widget2.get(), view2);
view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 2"));
EXPECT_EQ(widget2->GetNativeView()->GetRootWindow(), root_windows[1]);
// Show tooltip on second display.
aura::test::EventGenerator generator(root_windows[1]);
generator.MoveMouseRelativeTo(widget2->GetNativeView(),
view2->bounds().CenterPoint());
FireTooltipTimer();
EXPECT_TRUE(IsTooltipVisible());
// Get rid of secondary display. This destroy's the tooltip's aura window. If
// we have handled this case, we will not crash in the following statement.
UpdateDisplay("1000x600");
EXPECT_FALSE(IsTooltipVisible());
EXPECT_EQ(widget2->GetNativeView()->GetRootWindow(), root_windows[0]);
// The tooltip should create a new aura window for itself, so we should still
// be able to show tooltips on the primary display.
aura::test::EventGenerator generator1(root_windows[0]);
generator1.MoveMouseRelativeTo(widget1->GetNativeView(),
view1->bounds().CenterPoint());
FireTooltipTimer();
EXPECT_TRUE(IsTooltipVisible());
}
} // namespace test } // namespace test
} // 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