| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ui/views/cocoa/bridged_native_widget.h" | 5 #import "ui/views/cocoa/bridged_native_widget.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 #import <Quartz/Quartz.h> |
| 8 | 9 |
| 9 #import "base/mac/foundation_util.h" | 10 #import "base/mac/foundation_util.h" |
| 10 #import "base/mac/mac_util.h" | 11 #import "base/mac/mac_util.h" |
| 11 #import "base/mac/sdk_forward_declarations.h" | 12 #import "base/mac/sdk_forward_declarations.h" |
| 12 #include "base/macros.h" | 13 #include "base/macros.h" |
| 13 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 15 #include "base/strings/sys_string_conversions.h" | 16 #include "base/strings/sys_string_conversions.h" |
| 16 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 17 #import "testing/gtest_mac.h" | 18 #import "testing/gtest_mac.h" |
| 19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSBezierPath+CGPath.h" |
| 18 #import "ui/base/cocoa/window_size_constants.h" | 20 #import "ui/base/cocoa/window_size_constants.h" |
| 19 #include "ui/base/ime/input_method.h" | 21 #include "ui/base/ime/input_method.h" |
| 20 #import "ui/gfx/test/ui_cocoa_test_helper.h" | 22 #import "ui/gfx/test/ui_cocoa_test_helper.h" |
| 21 #import "ui/gfx/mac/coordinate_conversion.h" | 23 #import "ui/gfx/mac/coordinate_conversion.h" |
| 24 #include "ui/views/bubble/bubble_delegate.h" |
| 22 #import "ui/views/cocoa/bridged_content_view.h" | 25 #import "ui/views/cocoa/bridged_content_view.h" |
| 23 #import "ui/views/cocoa/native_widget_mac_nswindow.h" | 26 #import "ui/views/cocoa/native_widget_mac_nswindow.h" |
| 24 #import "ui/views/cocoa/views_nswindow_delegate.h" | 27 #import "ui/views/cocoa/views_nswindow_delegate.h" |
| 25 #include "ui/views/controls/textfield/textfield.h" | 28 #include "ui/views/controls/textfield/textfield.h" |
| 26 #include "ui/views/view.h" | 29 #include "ui/views/view.h" |
| 27 #include "ui/views/widget/native_widget_mac.h" | 30 #include "ui/views/widget/native_widget_mac.h" |
| 28 #include "ui/views/widget/root_view.h" | 31 #include "ui/views/widget/root_view.h" |
| 29 #include "ui/views/widget/widget.h" | 32 #include "ui/views/widget/widget.h" |
| 30 #include "ui/views/widget/widget_observer.h" | 33 #include "ui/views/widget/widget_observer.h" |
| 31 | 34 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 public: | 157 public: |
| 155 BridgedNativeWidgetTestBase() | 158 BridgedNativeWidgetTestBase() |
| 156 : widget_(new Widget), | 159 : widget_(new Widget), |
| 157 native_widget_mac_(new MockNativeWidgetMac(widget_.get())) { | 160 native_widget_mac_(new MockNativeWidgetMac(widget_.get())) { |
| 158 } | 161 } |
| 159 | 162 |
| 160 scoped_ptr<BridgedNativeWidget>& bridge() { | 163 scoped_ptr<BridgedNativeWidget>& bridge() { |
| 161 return native_widget_mac_->bridge(); | 164 return native_widget_mac_->bridge(); |
| 162 } | 165 } |
| 163 | 166 |
| 164 // Overridden from testing::Test: | 167 // Can be overriden in derived classes to provide additional |init_params_|. |
| 165 void SetUp() override { | 168 virtual void InitParams() { |
| 166 ui::CocoaTest::SetUp(); | |
| 167 | |
| 168 init_params_.native_widget = native_widget_mac_; | 169 init_params_.native_widget = native_widget_mac_; |
| 169 | 170 |
| 170 // Use a frameless window, otherwise Widget will try to center the window | 171 // Use a frameless window, otherwise Widget will try to center the window |
| 171 // before the tests covering the Init() flow are ready to do that. | 172 // before the tests covering the Init() flow are ready to do that. |
| 172 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; | 173 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
| 173 | 174 |
| 174 // To control the lifetime without an actual window that must be closed, | 175 // To control the lifetime without an actual window that must be closed, |
| 175 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. | 176 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. |
| 176 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 177 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 177 | 178 |
| 178 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. | 179 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. |
| 179 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; | 180 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; |
| 180 | 181 |
| 181 init_params_.bounds = gfx::Rect(100, 100, 100, 100); | 182 init_params_.bounds = gfx::Rect(100, 100, 100, 100); |
| 183 } |
| 182 | 184 |
| 185 // Overridden from testing::Test: |
| 186 void SetUp() override { |
| 187 ui::CocoaTest::SetUp(); |
| 188 InitParams(); |
| 183 native_widget_mac_->GetWidget()->Init(init_params_); | 189 native_widget_mac_->GetWidget()->Init(init_params_); |
| 184 } | 190 } |
| 185 | 191 |
| 186 protected: | 192 protected: |
| 187 scoped_ptr<Widget> widget_; | 193 scoped_ptr<Widget> widget_; |
| 188 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. | 194 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. |
| 189 | 195 |
| 190 // Make the InitParams available to tests to cover initialization codepaths. | 196 // Make the InitParams available to tests to cover initialization codepaths. |
| 191 Widget::InitParams init_params_; | 197 Widget::InitParams init_params_; |
| 192 }; | 198 }; |
| 193 | 199 |
| 194 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { | 200 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { |
| 195 public: | 201 public: |
| 196 BridgedNativeWidgetTest(); | 202 BridgedNativeWidgetTest(); |
| 197 ~BridgedNativeWidgetTest() override; | 203 ~BridgedNativeWidgetTest() override; |
| 198 | 204 |
| 199 // Install a textfield with input type |text_input_type| in the view hierarchy | 205 // Install a textfield with input type |text_input_type| in the view hierarchy |
| 200 // and make it the text input client. | 206 // and make it the text input client. |
| 201 void InstallTextField(const std::string& text, | 207 void InstallTextField(const std::string& text, |
| 202 ui::TextInputType text_input_type); | 208 ui::TextInputType text_input_type); |
| 203 | 209 |
| 204 // Install a textfield with input type ui::TEXT_INPUT_TYPE_TEXT in the view | 210 // Install a textfield with input type ui::TEXT_INPUT_TYPE_TEXT in the view |
| 205 // hierarchy and make it the text input client. | 211 // hierarchy and make it the text input client. |
| 206 void InstallTextField(const std::string& text); | 212 void InstallTextField(const std::string& text); |
| 207 | 213 |
| 208 // Returns the current text as std::string. | 214 // Returns the current text as std::string. |
| 209 std::string GetText(); | 215 std::string GetText(); |
| 210 | 216 |
| 217 // Returns the mask layer associated with bridge(). |
| 218 CAShapeLayer* GetMaskLayer(); |
| 219 |
| 211 // testing::Test: | 220 // testing::Test: |
| 212 void SetUp() override; | 221 void SetUp() override; |
| 213 void TearDown() override; | 222 void TearDown() override; |
| 214 | 223 |
| 215 protected: | 224 protected: |
| 216 scoped_ptr<views::View> view_; | 225 scoped_ptr<views::View> view_; |
| 217 scoped_ptr<BridgedNativeWidget> bridge_; | 226 scoped_ptr<BridgedNativeWidget> bridge_; |
| 218 BridgedContentView* ns_view_; // Weak. Owned by bridge_. | 227 BridgedContentView* ns_view_; // Weak. Owned by bridge_. |
| 219 base::MessageLoopForUI message_loop_; | 228 base::MessageLoopForUI message_loop_; |
| 220 | 229 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 250 InstallTextField(text, ui::TEXT_INPUT_TYPE_TEXT); | 259 InstallTextField(text, ui::TEXT_INPUT_TYPE_TEXT); |
| 251 } | 260 } |
| 252 | 261 |
| 253 std::string BridgedNativeWidgetTest::GetText() { | 262 std::string BridgedNativeWidgetTest::GetText() { |
| 254 NSRange range = NSMakeRange(0, NSUIntegerMax); | 263 NSRange range = NSMakeRange(0, NSUIntegerMax); |
| 255 NSAttributedString* text = | 264 NSAttributedString* text = |
| 256 [ns_view_ attributedSubstringForProposedRange:range actualRange:NULL]; | 265 [ns_view_ attributedSubstringForProposedRange:range actualRange:NULL]; |
| 257 return SysNSStringToUTF8([text string]); | 266 return SysNSStringToUTF8([text string]); |
| 258 } | 267 } |
| 259 | 268 |
| 269 CAShapeLayer* BridgedNativeWidgetTest::GetMaskLayer() { |
| 270 return base::mac::ObjCCastStrict<CAShapeLayer>( |
| 271 [[bridge()->compositor_superview_ layer] mask]); |
| 272 } |
| 273 |
| 260 void BridgedNativeWidgetTest::SetUp() { | 274 void BridgedNativeWidgetTest::SetUp() { |
| 261 BridgedNativeWidgetTestBase::SetUp(); | 275 BridgedNativeWidgetTestBase::SetUp(); |
| 262 | 276 |
| 263 view_.reset(new views::internal::RootView(widget_.get())); | 277 view_.reset(new views::internal::RootView(widget_.get())); |
| 264 base::scoped_nsobject<NSWindow> window([test_window() retain]); | 278 base::scoped_nsobject<NSWindow> window([test_window() retain]); |
| 265 | 279 |
| 266 // BridgedNativeWidget expects to be initialized with a hidden (deferred) | 280 // BridgedNativeWidget expects to be initialized with a hidden (deferred) |
| 267 // window. | 281 // window. |
| 268 [window orderOut:nil]; | 282 [window orderOut:nil]; |
| 269 EXPECT_FALSE([window delegate]); | 283 EXPECT_FALSE([window delegate]); |
| 270 bridge()->Init(window, init_params_); | 284 bridge()->Init(window, init_params_); |
| 271 | 285 |
| 272 // The delegate should exist before setting the root view. | 286 // The delegate should exist before setting the root view. |
| 273 EXPECT_TRUE([window delegate]); | 287 EXPECT_TRUE([window delegate]); |
| 274 bridge()->SetRootView(view_.get()); | 288 bridge()->SetRootView(view_.get()); |
| 275 ns_view_ = bridge()->ns_view(); | 289 ns_view_ = bridge()->ns_view(); |
| 276 | 290 |
| 291 bridge()->AddCompositorSuperview(); |
| 292 |
| 277 // Pretend it has been shown via NativeWidgetMac::Show(). | 293 // Pretend it has been shown via NativeWidgetMac::Show(). |
| 278 [window orderFront:nil]; | 294 [window orderFront:nil]; |
| 279 [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()]; | 295 [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()]; |
| 280 } | 296 } |
| 281 | 297 |
| 282 void BridgedNativeWidgetTest::TearDown() { | 298 void BridgedNativeWidgetTest::TearDown() { |
| 283 view_.reset(); | 299 view_.reset(); |
| 284 BridgedNativeWidgetTestBase::TearDown(); | 300 BridgedNativeWidgetTestBase::TearDown(); |
| 285 } | 301 } |
| 286 | 302 |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 744 [window_delegate windowDidFailToExitFullScreen:window]; | 760 [window_delegate windowDidFailToExitFullScreen:window]; |
| 745 EXPECT_FALSE(bridge()->target_fullscreen_state()); | 761 EXPECT_FALSE(bridge()->target_fullscreen_state()); |
| 746 [center postNotificationName:NSWindowDidExitFullScreenNotification | 762 [center postNotificationName:NSWindowDidExitFullScreenNotification |
| 747 object:window]; | 763 object:window]; |
| 748 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. | 764 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. |
| 749 EXPECT_FALSE(bridge()->target_fullscreen_state()); | 765 EXPECT_FALSE(bridge()->target_fullscreen_state()); |
| 750 | 766 |
| 751 widget_->CloseNow(); | 767 widget_->CloseNow(); |
| 752 } | 768 } |
| 753 | 769 |
| 770 TEST_F(BridgedNativeWidgetTest, NoMaskLayer) { |
| 771 // BridgedNativeWidgetTest creates a frameless window which does not have a |
| 772 // non-client view. Hence it shouldn't have a mask layer. |
| 773 EXPECT_FALSE(GetMaskLayer()); |
| 774 } |
| 775 |
| 776 // This class provides a bubble delegate to the widget, which has a non-empty |
| 777 // window mask. |
| 778 class BridgedNativeWidgetMaskTest : public BridgedNativeWidgetTest { |
| 779 public: |
| 780 void InitParams() override { |
| 781 BridgedNativeWidgetTest::InitParams(); |
| 782 init_params_.type = Widget::InitParams::TYPE_BUBBLE; |
| 783 init_params_.delegate = new BubbleDelegateView(); |
| 784 } |
| 785 }; |
| 786 |
| 787 TEST_F(BridgedNativeWidgetMaskTest, MaskLayer) { |
| 788 CAShapeLayer* mask_layer = GetMaskLayer(); |
| 789 EXPECT_TRUE(mask_layer); |
| 790 EXPECT_TRUE([mask_layer path]); |
| 791 EXPECT_TRUE( |
| 792 CGPathEqualToPath([[ns_view_ windowMask] gtm_CGPath], [mask_layer path])); |
| 793 |
| 794 CGPathRef prev_path = [mask_layer path]; |
| 795 |
| 796 // Resize the window and ensure the mask path changes accordingly. |
| 797 const int kTestNewWidth = 400; |
| 798 const int kTestNewHeight = 300; |
| 799 NSRect new_frame = NSMakeRect(0, 0, kTestNewWidth, kTestNewHeight); |
| 800 EXPECT_NSNE(new_frame, [test_window() frame]); |
| 801 [test_window() setFrame:new_frame display:NO]; |
| 802 mask_layer = GetMaskLayer(); |
| 803 |
| 804 EXPECT_FALSE(CGPathEqualToPath(prev_path, [mask_layer path])); |
| 805 EXPECT_TRUE([mask_layer path]); |
| 806 EXPECT_TRUE( |
| 807 CGPathEqualToPath([[ns_view_ windowMask] gtm_CGPath], [mask_layer path])); |
| 808 } |
| 809 |
| 754 } // namespace test | 810 } // namespace test |
| 755 } // namespace views | 811 } // namespace views |
| OLD | NEW |