| Index: ui/views/widget/native_widget_mac_unittest.mm
|
| diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
|
| index 5a48f0257223f75064732e12b1a76061c18a7cd2..453312e0e10b776582086a40d0e523306071e82b 100644
|
| --- a/ui/views/widget/native_widget_mac_unittest.mm
|
| +++ b/ui/views/widget/native_widget_mac_unittest.mm
|
| @@ -22,6 +22,7 @@
|
| #import "ui/events/test/cocoa_test_event_utils.h"
|
| #include "ui/events/test/event_generator.h"
|
| #import "ui/gfx/mac/coordinate_conversion.h"
|
| +#include "ui/views/bubble/bubble_delegate.h"
|
| #import "ui/views/cocoa/bridged_native_widget.h"
|
| #import "ui/views/cocoa/native_widget_mac_nswindow.h"
|
| #include "ui/views/controls/button/label_button.h"
|
| @@ -918,6 +919,94 @@ TEST_F(NativeWidgetMacTest, NoopReparentNativeView) {
|
| parent_widget->CloseNow();
|
| }
|
|
|
| +// Attaches a child window to |parent| that checks its parent's delegate is
|
| +// cleared when the child is destroyed. This assumes the child is destroyed via
|
| +// destruction of its parent.
|
| +class ParentCloseMonitor : public WidgetObserver {
|
| + public:
|
| + explicit ParentCloseMonitor(Widget* parent) {
|
| + Widget* child = new Widget();
|
| + child->AddObserver(this);
|
| + Widget::InitParams init_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
|
| + init_params.parent = parent->GetNativeView();
|
| + init_params.bounds = gfx::Rect(100, 100, 100, 100);
|
| + init_params.native_widget = new NativeWidgetCapture(child);
|
| + child->Init(init_params);
|
| + child->Show();
|
| +
|
| + // NSWindow parent/child relationship should be established on Show() and
|
| + // the parent should have a delegate. Retain the parent since it can't be
|
| + // retrieved from the child while it is being destroyed.
|
| + parent_nswindow_.reset([[child->GetNativeWindow() parentWindow] retain]);
|
| + EXPECT_TRUE(parent_nswindow_);
|
| + EXPECT_TRUE([parent_nswindow_ delegate]);
|
| + }
|
| +
|
| + ~ParentCloseMonitor() override {
|
| + EXPECT_TRUE(child_closed_); // Otherwise the observer wasn't removed.
|
| + }
|
| +
|
| + void OnWidgetDestroying(Widget* child) override {
|
| + // Upon a parent-triggered close, the NSWindow relationship will already be
|
| + // removed. The parent should still be open (children are always closed
|
| + // first), but not have a delegate (since it is being torn down).
|
| + EXPECT_FALSE([child->GetNativeWindow() parentWindow]);
|
| + EXPECT_TRUE([parent_nswindow_ isVisible]);
|
| + EXPECT_FALSE([parent_nswindow_ delegate]);
|
| +
|
| + EXPECT_FALSE(child_closed_);
|
| + child->RemoveObserver(this);
|
| + child_closed_ = true;
|
| + }
|
| +
|
| + bool child_closed() const { return child_closed_; }
|
| +
|
| + private:
|
| + base::scoped_nsobject<NSWindow> parent_nswindow_;
|
| + bool child_closed_ = false;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ParentCloseMonitor);
|
| +};
|
| +
|
| +// Ensures when a parent window is destroyed, and triggers its child windows to
|
| +// be closed, that the child windows (via AppKit) do not attempt to call back
|
| +// into the parent, whilst it's in the process of being destroyed.
|
| +TEST_F(NativeWidgetMacTest, NoParentDelegateDuringTeardown) {
|
| + // First test "normal" windows and AppKit close.
|
| + {
|
| + Widget* parent = CreateTopLevelPlatformWidget();
|
| + parent->SetBounds(gfx::Rect(100, 100, 300, 200));
|
| + parent->Show();
|
| + ParentCloseMonitor monitor(parent);
|
| + [parent->GetNativeWindow() close];
|
| + EXPECT_TRUE(monitor.child_closed());
|
| + }
|
| +
|
| + // Test the Widget::CloseNow() flow.
|
| + {
|
| + Widget* parent = CreateTopLevelPlatformWidget();
|
| + parent->SetBounds(gfx::Rect(100, 100, 300, 200));
|
| + parent->Show();
|
| + ParentCloseMonitor monitor(parent);
|
| + parent->CloseNow();
|
| + EXPECT_TRUE(monitor.child_closed());
|
| + }
|
| +
|
| + // Test the WIDGET_OWNS_NATIVE_WIDGET flow.
|
| + {
|
| + scoped_ptr<Widget> parent(new Widget);
|
| + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
|
| + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
| + params.bounds = gfx::Rect(100, 100, 300, 200);
|
| + parent->Init(params);
|
| + parent->Show();
|
| +
|
| + ParentCloseMonitor monitor(parent.get());
|
| + parent.reset();
|
| + EXPECT_TRUE(monitor.child_closed());
|
| + }
|
| +}
|
| +
|
| // Tests Cocoa properties that should be given to particular widget types.
|
| TEST_F(NativeWidgetMacTest, NativeProperties) {
|
| // Create a regular widget (TYPE_WINDOW).
|
| @@ -938,6 +1027,20 @@ TEST_F(NativeWidgetMacTest, NativeProperties) {
|
| // Dialogs shouldn't take main status away from their parent.
|
| EXPECT_FALSE([dialog_widget->GetNativeWindow() canBecomeMainWindow]);
|
|
|
| + // Create a bubble widget with a parent: also shouldn't get main.
|
| + BubbleDelegateView* bubble_view = new BubbleDelegateView();
|
| + bubble_view->set_parent_window(regular_widget->GetNativeView());
|
| + Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_view);
|
| + EXPECT_TRUE([bubble_widget->GetNativeWindow() canBecomeKeyWindow]);
|
| + EXPECT_FALSE([bubble_widget->GetNativeWindow() canBecomeMainWindow]);
|
| +
|
| + // But a bubble without a parent should still be able to become main.
|
| + Widget* toplevel_bubble_widget =
|
| + BubbleDelegateView::CreateBubble(new BubbleDelegateView());
|
| + EXPECT_TRUE([toplevel_bubble_widget->GetNativeWindow() canBecomeKeyWindow]);
|
| + EXPECT_TRUE([toplevel_bubble_widget->GetNativeWindow() canBecomeMainWindow]);
|
| +
|
| + toplevel_bubble_widget->CloseNow();
|
| regular_widget->CloseNow();
|
| }
|
|
|
|
|