summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortapted@chromium.org <tapted@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 06:58:50 +0000
committertapted@chromium.org <tapted@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 06:58:50 +0000
commit8c2b828673030fe2894e5f57ef31c6621b964e2c (patch)
tree330c29ca410f496917403c8d073473538557129d
parent1efe6f9efa0fa1cdc21bd1e5a349ad6c27e4fe43 (diff)
downloadchromium_src-8c2b828673030fe2894e5f57ef31c6621b964e2c.zip
chromium_src-8c2b828673030fe2894e5f57ef31c6621b964e2c.tar.gz
chromium_src-8c2b828673030fe2894e5f57ef31c6621b964e2c.tar.bz2
MacViews: Implement toolkit-views style NSWindow ownership.
MacViews currently releases the NSWindow as a side-effect of them being closed. This CL implements ownership the way toolkit-views desires. That is, according to the Widget::InitParams for |ownership| (Widget::Ownership) and |parent| (a gfx::NativeView -> NSView*). Native close events are observed through the NSWindowDelegate. Destroying the BridgedNativeView wrapper guarantees the native window is closed. Similarly, closing an NSWindow guarantees that the bridge is destroyed. This matches the aura::Window behaviour closely. Note that on Mac, "closed" can not imply "destroyed" due to reference counting, but references to anything Chrome owns are cleared out when the bridge is destroyed. Parent windows are implemented on top of -[NSWindow childWindows], which provides window management for relative layering and origin coordinates of children and their parents. Destroying a bridge to an NSWindow with children will close all child windows. This CL is covered by tests 3*WidgetChildDestructionTest.* 5*WidgetOwnershipTest.* 3*WidgetTest.{GetAllChildWidgets, GetTopLevelWidget_Native, SingleWindowClosing}. Some new BridgedNativeWidget tests are also added for special cases. A few views_unittests also now pass incidentally. Before: 409 tests run 53 tests failed 26 tests crashed. After: 411 tests run 39 tests failed 24 tests crashed. BUG=378134 TEST=views_unittests Review URL: https://codereview.chromium.org/344443010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279645 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ui/views/cocoa/bridged_native_widget.h12
-rw-r--r--ui/views/cocoa/bridged_native_widget.mm37
-rw-r--r--ui/views/cocoa/bridged_native_widget_unittest.mm123
-rw-r--r--ui/views/cocoa/views_nswindow_delegate.mm7
-rw-r--r--ui/views/test/views_test_base.h3
-rw-r--r--ui/views/test/widget_test_mac.mm8
-rw-r--r--ui/views/widget/native_widget_mac.h15
-rw-r--r--ui/views/widget/native_widget_mac.mm74
-rw-r--r--ui/views/widget/widget_unittest.cc3
9 files changed, 241 insertions, 41 deletions
diff --git a/ui/views/cocoa/bridged_native_widget.h b/ui/views/cocoa/bridged_native_widget.h
index 88c8684..3e21bbf 100644
--- a/ui/views/cocoa/bridged_native_widget.h
+++ b/ui/views/cocoa/bridged_native_widget.h
@@ -11,6 +11,7 @@
#include "base/memory/scoped_ptr.h"
#include "ui/views/ime/input_method_delegate.h"
#include "ui/views/views_export.h"
+#include "ui/views/widget/widget.h"
@class BridgedContentView;
@class ViewsNSWindowDelegate;
@@ -30,17 +31,21 @@ class View;
// NativeWidgetMac to the Cocoa window. Behaves a bit like an aura::Window.
class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate {
public:
- // Creates one side of the bridge. |parent| can be NULL in tests.
+ // Creates one side of the bridge. |parent| must not be NULL.
explicit BridgedNativeWidget(NativeWidgetMac* parent);
virtual ~BridgedNativeWidget();
// Initialize the bridge, "retains" ownership of |window|.
- void Init(base::scoped_nsobject<NSWindow> window);
+ void Init(base::scoped_nsobject<NSWindow> window,
+ const Widget::InitParams& params);
// Set or clears the views::View bridged by the content view. This does NOT
// take ownership of |view|.
void SetRootView(views::View* view);
+ // Called internally by the NSWindowDelegate when the window is closing.
+ void OnWindowWillClose();
+
// See widget.h for documentation.
InputMethod* CreateInputMethod();
ui::InputMethod* GetHostInputMethod();
@@ -53,6 +58,9 @@ class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate {
virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) OVERRIDE;
private:
+ // Closes all child windows. BridgedNativeWidget children will be destroyed.
+ void RemoveOrDestroyChildren();
+
views::NativeWidgetMac* native_widget_mac_; // Weak. Owns this.
base::scoped_nsobject<NSWindow> window_;
base::scoped_nsobject<ViewsNSWindowDelegate> window_delegate_;
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 4bfb0ba..7695a66 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -10,28 +10,43 @@
#include "ui/base/ui_base_switches_util.h"
#import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/views_nswindow_delegate.h"
+#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/ime/input_method_bridge.h"
#include "ui/views/ime/null_input_method.h"
#include "ui/views/view.h"
-#include "ui/views/widget/widget.h"
namespace views {
BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
: native_widget_mac_(parent) {
+ DCHECK(parent);
window_delegate_.reset(
[[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
}
BridgedNativeWidget::~BridgedNativeWidget() {
+ RemoveOrDestroyChildren();
SetRootView(NULL);
- [window_ setDelegate:nil];
+ if ([window_ delegate]) {
+ // If the delegate is still set, it means OnWindowWillClose has not been
+ // called and the window is still open. Calling -[NSWindow close] will
+ // synchronously call OnWindowWillClose and notify NativeWidgetMac.
+ [window_ close];
+ }
+ DCHECK(![window_ delegate]);
}
-void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window) {
+void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window,
+ const Widget::InitParams& params) {
DCHECK(!window_);
window_.swap(window);
[window_ setDelegate:window_delegate_];
+
+ if (params.parent) {
+ // Use NSWindow to manage child windows. This won't automatically close them
+ // but it will maintain relative positioning of the window layer and origin.
+ [[params.parent window] addChildWindow:window_ ordered:NSWindowAbove];
+ }
}
void BridgedNativeWidget::SetRootView(views::View* view) {
@@ -53,6 +68,12 @@ void BridgedNativeWidget::SetRootView(views::View* view) {
[window_ setContentView:bridged_view_];
}
+void BridgedNativeWidget::OnWindowWillClose() {
+ [[window_ parentWindow] removeChildWindow:window_];
+ [window_ setDelegate:nil];
+ native_widget_mac_->OnWindowWillClose();
+}
+
InputMethod* BridgedNativeWidget::CreateInputMethod() {
if (switches::IsTextInputFocusManagerEnabled())
return new NullInputMethod();
@@ -81,4 +102,14 @@ void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) {
widget->GetFocusManager()->OnKeyEvent(key);
}
+////////////////////////////////////////////////////////////////////////////////
+// BridgedNativeWidget, private:
+
+void BridgedNativeWidget::RemoveOrDestroyChildren() {
+ // TODO(tapted): Implement unowned child windows if required.
+ base::scoped_nsobject<NSArray> child_windows(
+ [[NSArray alloc] initWithArray:[window_ childWindows]]);
+ [child_windows makeObjectsPerformSelector:@selector(close)];
+}
+
} // namespace views
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
index efc80d3..22107d7 100644
--- a/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -12,10 +12,70 @@
#import "ui/views/cocoa/bridged_content_view.h"
#include "ui/views/ime/input_method.h"
#include "ui/views/view.h"
+#include "ui/views/widget/native_widget_mac.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
namespace views {
+namespace test {
-class BridgedNativeWidgetTest : public ui::CocoaTest {
+// Provides the |parent| argument to construct a BridgedNativeWidget.
+class MockNativeWidgetMac : public NativeWidgetMac {
+ public:
+ MockNativeWidgetMac(Widget* delegate) : NativeWidgetMac(delegate) {}
+
+ // Expose a reference, so that it can be reset() independently.
+ scoped_ptr<BridgedNativeWidget>& bridge() {
+ return bridge_;
+ }
+
+ // internal::NativeWidgetPrivate:
+ virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE {
+ ownership_ = params.ownership;
+
+ // Usually the bridge gets initialized here. It is skipped to run extra
+ // checks in tests, and so that a second window isn't created.
+ delegate()->OnNativeWidgetCreated(true);
+ }
+
+ virtual void ReorderNativeViews() OVERRIDE {
+ // Called via Widget::Init to set the content view. No-op in these tests.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockNativeWidgetMac);
+};
+
+// Helper test base to construct a BridgedNativeWidget with a valid parent.
+class BridgedNativeWidgetTestBase : public ui::CocoaTest {
+ public:
+ BridgedNativeWidgetTestBase()
+ : widget_(new Widget),
+ native_widget_mac_(new MockNativeWidgetMac(widget_.get())) {
+ }
+
+ scoped_ptr<BridgedNativeWidget>& bridge() {
+ return native_widget_mac_->bridge();
+ }
+
+ // Overridden from testing::Test:
+ virtual void SetUp() OVERRIDE {
+ ui::CocoaTest::SetUp();
+
+ Widget::InitParams params;
+ params.native_widget = native_widget_mac_;
+ // To control the lifetime without an actual window that must be closed,
+ // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET.
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ native_widget_mac_->GetWidget()->Init(params);
+ }
+
+ protected:
+ scoped_ptr<Widget> widget_;
+ MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|.
+};
+
+class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase {
public:
BridgedNativeWidgetTest();
virtual ~BridgedNativeWidgetTest();
@@ -27,7 +87,6 @@ class BridgedNativeWidgetTest : public ui::CocoaTest {
protected:
// TODO(tapted): Make this a EventCountView from widget_unittest.cc.
scoped_ptr<views::View> view_;
- scoped_ptr<BridgedNativeWidget> bridge_;
private:
DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTest);
@@ -40,36 +99,31 @@ BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
}
void BridgedNativeWidgetTest::SetUp() {
- ui::CocoaTest::SetUp();
+ BridgedNativeWidgetTestBase::SetUp();
view_.reset(new views::View);
- bridge_.reset(new BridgedNativeWidget(NULL));
base::scoped_nsobject<NSWindow> window([test_window() retain]);
EXPECT_FALSE([window delegate]);
- bridge_->Init(window);
+ bridge()->Init(window, Widget::InitParams());
// The delegate should exist before setting the root view.
EXPECT_TRUE([window delegate]);
- bridge_->SetRootView(view_.get());
+ bridge()->SetRootView(view_.get());
- [test_window() makePretendKeyWindowAndSetFirstResponder:bridge_->ns_view()];
+ [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()];
}
void BridgedNativeWidgetTest::TearDown() {
- [test_window() clearPretendKeyWindowAndFirstResponder];
-
- bridge_.reset();
view_.reset();
-
- ui::CocoaTest::TearDown();
+ BridgedNativeWidgetTestBase::TearDown();
}
// The TEST_VIEW macro expects the view it's testing to have a superview. In
// these tests, the NSView bridge is a contentView, at the root. These mimic
// what TEST_VIEW usually does.
TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewAddRemove) {
- base::scoped_nsobject<BridgedContentView> view([bridge_->ns_view() retain]);
+ base::scoped_nsobject<BridgedContentView> view([bridge()->ns_view() retain]);
EXPECT_NSEQ([test_window() contentView], view);
EXPECT_NSEQ(test_window(), [view window]);
@@ -79,7 +133,7 @@ TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewAddRemove) {
// Destroying the C++ bridge should remove references to any C++ objects in
// the ObjectiveC object, and remove it from the hierarchy.
- bridge_.reset();
+ bridge().reset();
EXPECT_FALSE([view hostedView]);
EXPECT_FALSE([view superview]);
EXPECT_FALSE([view window]);
@@ -88,7 +142,7 @@ TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewAddRemove) {
}
TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewDisplay) {
- [bridge_->ns_view() display];
+ [bridge()->ns_view() display];
}
// Test that resizing the window resizes the root view appropriately.
@@ -112,12 +166,47 @@ TEST_F(BridgedNativeWidgetTest, ViewSizeTracksWindow) {
}
TEST_F(BridgedNativeWidgetTest, CreateInputMethod) {
- scoped_ptr<views::InputMethod> input_method(bridge_->CreateInputMethod());
+ scoped_ptr<views::InputMethod> input_method(bridge()->CreateInputMethod());
EXPECT_TRUE(input_method);
}
TEST_F(BridgedNativeWidgetTest, GetHostInputMethod) {
- EXPECT_TRUE(bridge_->GetHostInputMethod());
+ EXPECT_TRUE(bridge()->GetHostInputMethod());
+}
+
+// A simpler test harness for testing initialization flows.
+typedef BridgedNativeWidgetTestBase BridgedNativeWidgetInitTest;
+
+// Test that BridgedNativeWidget remains sane if Init() is never called.
+TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) {
+ EXPECT_FALSE(bridge()->ns_view());
+ EXPECT_FALSE(bridge()->ns_window());
+ bridge().reset();
+}
+
+// Test attaching to a parent window that is not a NativeWidgetMac. When the
+// parent is a NativeWidgetMac, that is covered in widget_unittest.cc by
+// WidgetOwnershipTest.Ownership_ViewsNativeWidgetOwnsWidget*.
+TEST_F(BridgedNativeWidgetInitTest, ParentWindowNotNativeWidgetMac) {
+ Widget::InitParams params;
+ params.parent = [test_window() contentView];
+ EXPECT_EQ(0u, [[test_window() childWindows] count]);
+
+ base::scoped_nsobject<NSWindow> child_window(
+ [[NSWindow alloc] initWithContentRect:NSMakeRect(50, 50, 400, 300)
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO]);
+ [child_window setReleasedWhenClosed:NO]; // Owned by scoped_nsobject.
+
+ EXPECT_FALSE([child_window parentWindow]);
+ bridge()->Init(child_window, params);
+
+ EXPECT_EQ(1u, [[test_window() childWindows] count]);
+ EXPECT_EQ(test_window(), [bridge()->ns_window() parentWindow]);
+ bridge().reset();
+ EXPECT_EQ(0u, [[test_window() childWindows] count]);
}
+} // namespace test
} // namespace views
diff --git a/ui/views/cocoa/views_nswindow_delegate.mm b/ui/views/cocoa/views_nswindow_delegate.mm
index 87717c0..da577d6 100644
--- a/ui/views/cocoa/views_nswindow_delegate.mm
+++ b/ui/views/cocoa/views_nswindow_delegate.mm
@@ -21,4 +21,11 @@
return parent_->native_widget_mac();
}
+// NSWindowDelegate implementation.
+
+- (void)windowWillClose:(NSNotification*)notification {
+ DCHECK([parent_->ns_window() isEqual:[notification object]]);
+ parent_->OnWindowWillClose();
+}
+
@end
diff --git a/ui/views/test/views_test_base.h b/ui/views/test/views_test_base.h
index f689ea0..814ee11 100644
--- a/ui/views/test/views_test_base.h
+++ b/ui/views/test/views_test_base.h
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
#include "ui/views/test/test_views_delegate.h"
#if defined(OS_WIN)
@@ -20,7 +21,7 @@ class ViewsTestHelper;
// A base class for views unit test. It creates a message loop necessary
// to drive UI events and takes care of OLE initialization for windows.
-class ViewsTestBase : public testing::Test {
+class ViewsTestBase : public PlatformTest {
public:
ViewsTestBase();
virtual ~ViewsTestBase();
diff --git a/ui/views/test/widget_test_mac.mm b/ui/views/test/widget_test_mac.mm
index 84f2bd0..3a3017b 100644
--- a/ui/views/test/widget_test_mac.mm
+++ b/ui/views/test/widget_test_mac.mm
@@ -6,6 +6,7 @@
#include <Cocoa/Cocoa.h>
+#import "base/mac/scoped_nsobject.h"
#include "ui/views/widget/root_view.h"
namespace views {
@@ -13,8 +14,11 @@ namespace test {
// static
void WidgetTest::SimulateNativeDestroy(Widget* widget) {
- DCHECK([widget->GetNativeWindow() isReleasedWhenClosed]);
- [widget->GetNativeWindow() close];
+ // Retain the window while closing it, otherwise the window may lose its last
+ // owner before -[NSWindow close] completes (this offends AppKit). Usually
+ // this reference will exist on an event delivered to the runloop.
+ base::scoped_nsobject<NSWindow> window([widget->GetNativeWindow() retain]);
+ [window close];
}
// static
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index 0cd87b7..2ce3f5e 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -9,6 +9,9 @@
#include "ui/views/widget/native_widget_private.h"
namespace views {
+namespace test {
+class MockNativeWidgetMac;
+}
class BridgedNativeWidget;
@@ -17,7 +20,10 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
NativeWidgetMac(internal::NativeWidgetDelegate* delegate);
virtual ~NativeWidgetMac();
- protected:
+ // Deletes |bridge_| and informs |delegate_| that the native widget is
+ // destroyed.
+ void OnWindowWillClose();
+
// internal::NativeWidgetPrivate:
virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE;
virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE;
@@ -104,10 +110,17 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
virtual void OnRootViewLayout() const OVERRIDE;
virtual void RepostNativeEvent(gfx::NativeEvent native_event) OVERRIDE;
+ protected:
+ internal::NativeWidgetDelegate* delegate() { return delegate_; }
+
private:
+ friend class test::MockNativeWidgetMac;
+
internal::NativeWidgetDelegate* delegate_;
scoped_ptr<BridgedNativeWidget> bridge_;
+ Widget::InitParams::Ownership ownership_;
+
DISALLOW_COPY_AND_ASSIGN(NativeWidgetMac);
};
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index c40b4757..c7f8108 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -20,16 +20,34 @@ namespace views {
// NativeWidgetMac, public:
NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate)
- : delegate_(delegate), bridge_(new BridgedNativeWidget(this)) {
+ : delegate_(delegate),
+ bridge_(new BridgedNativeWidget(this)),
+ ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
}
NativeWidgetMac::~NativeWidgetMac() {
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete delegate_;
+ else
+ CloseNow();
+}
+
+void NativeWidgetMac::OnWindowWillClose() {
+ delegate_->OnNativeWidgetDestroying();
+ // Note: If closed via CloseNow(), |bridge_| will already be reset. If closed
+ // by the user, or via Close() and a RunLoop, this will reset it.
+ bridge_.reset();
+ delegate_->OnNativeWidgetDestroyed();
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete this;
}
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMac, internal::NativeWidgetPrivate implementation:
void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
+ ownership_ = params.ownership;
+
// TODO(tapted): Convert position into Cocoa's flipped coordinate space.
NSRect content_rect =
NSMakeRect(0, 0, params.bounds.width(), params.bounds.height());
@@ -41,7 +59,10 @@ void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
styleMask:style_mask
backing:NSBackingStoreBuffered
defer:NO]);
- bridge_->Init(window);
+ [window setReleasedWhenClosed:NO]; // Owned by scoped_nsobject.
+ bridge_->Init(window, params);
+
+ delegate_->OnNativeWidgetCreated(true);
}
NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() {
@@ -75,12 +96,12 @@ gfx::NativeView NativeWidgetMac::GetNativeView() const {
}
gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const {
- return bridge_->ns_window();
+ return bridge_ ? bridge_->ns_window() : nil;
}
Widget* NativeWidgetMac::GetTopLevelWidget() {
- NOTIMPLEMENTED();
- return GetWidget();
+ NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
+ return native_widget ? native_widget->GetWidget() : NULL;
}
const ui::Compositor* NativeWidgetMac::GetCompositor() const {
@@ -99,7 +120,8 @@ ui::Layer* NativeWidgetMac::GetLayer() {
}
void NativeWidgetMac::ReorderNativeViews() {
- bridge_->SetRootView(GetWidget()->GetRootView());
+ if (bridge_)
+ bridge_->SetRootView(GetWidget()->GetRootView());
}
void NativeWidgetMac::ViewRemoved(View* view) {
@@ -134,7 +156,7 @@ bool NativeWidgetMac::HasCapture() const {
}
InputMethod* NativeWidgetMac::CreateInputMethod() {
- return bridge_->CreateInputMethod();
+ return bridge_ ? bridge_->CreateInputMethod() : NULL;
}
internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() {
@@ -142,7 +164,7 @@ internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() {
}
ui::InputMethod* NativeWidgetMac::GetHostInputMethod() {
- return bridge_->GetHostInputMethod();
+ return bridge_ ? bridge_->GetHostInputMethod() : NULL;
}
void NativeWidgetMac::CenterWindow(const gfx::Size& size) {
@@ -188,7 +210,7 @@ void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
}
void NativeWidgetMac::SetSize(const gfx::Size& size) {
- [bridge_->ns_window() setContentSize:NSMakeSize(size.width(), size.height())];
+ [GetNativeWindow() setContentSize:NSMakeSize(size.width(), size.height())];
}
void NativeWidgetMac::StackAbove(gfx::NativeView native_view) {
@@ -208,11 +230,15 @@ void NativeWidgetMac::SetShape(gfx::NativeRegion shape) {
}
void NativeWidgetMac::Close() {
- NOTIMPLEMENTED();
+ // Calling performClose: will momentarily highlight the close button.
+ [GetNativeWindow() performSelector:@selector(performClose:)
+ withObject:nil
+ afterDelay:0];
}
void NativeWidgetMac::CloseNow() {
- NOTIMPLEMENTED();
+ // Reset |bridge_| to NULL before destroying it.
+ scoped_ptr<BridgedNativeWidget> bridge(bridge_.Pass());
}
void NativeWidgetMac::Show() {
@@ -317,7 +343,7 @@ void NativeWidgetMac::RunShellDrag(View* view,
void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
// TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate
// system of |rect| has been converted.
- [bridge_->ns_view() setNeedsDisplay:YES];
+ [GetNativeView() setNeedsDisplay:YES];
}
void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) {
@@ -407,13 +433,33 @@ NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
// static
NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
gfx::NativeView native_view) {
- return GetNativeWidgetForNativeView(native_view);
+ NativeWidgetPrivate* native_widget =
+ GetNativeWidgetForNativeView(native_view);
+ if (!native_widget)
+ return NULL;
+
+ for (NativeWidgetPrivate* parent;
+ (parent = GetNativeWidgetForNativeWindow(
+ [native_widget->GetNativeWindow() parentWindow]));
+ native_widget = parent) {
+ }
+ return native_widget;
}
// static
void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
Widget::Widgets* children) {
- NOTIMPLEMENTED();
+ NativeWidgetPrivate* native_widget =
+ GetNativeWidgetForNativeView(native_view);
+ if (!native_widget)
+ return;
+
+ // Code expects widget for |native_view| to be added to |children|.
+ if (native_widget->GetWidget())
+ children->insert(native_widget->GetWidget());
+
+ for (NSWindow* child_window : [native_widget->GetNativeWindow() childWindows])
+ GetAllChildWidgets([child_window contentView], children);
}
// static
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index be93a8e..247ac75 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1678,8 +1678,9 @@ TEST_F(WidgetTest, CloseDestroys) {
widget->Show();
widget->Hide();
widget->Close();
+ EXPECT_FALSE(destroyed);
// Run the message loop as Close() asynchronously deletes.
- RunPendingMessages();
+ base::RunLoop().Run();
EXPECT_TRUE(destroyed);
// Close() should destroy the widget. If not we'll cleanup to avoid leaks.
if (!destroyed) {