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 | 8 |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/strings/sys_string_conversions.h" |
| 11 #include "base/strings/utf_string_conversions.h" |
10 #import "testing/gtest_mac.h" | 12 #import "testing/gtest_mac.h" |
11 #import "ui/gfx/test/ui_cocoa_test_helper.h" | 13 #import "ui/gfx/test/ui_cocoa_test_helper.h" |
12 #import "ui/views/cocoa/bridged_content_view.h" | 14 #import "ui/views/cocoa/bridged_content_view.h" |
| 15 #include "ui/views/controls/textfield/textfield.h" |
13 #include "ui/views/ime/input_method.h" | 16 #include "ui/views/ime/input_method.h" |
14 #include "ui/views/view.h" | 17 #include "ui/views/view.h" |
15 #include "ui/views/widget/native_widget_mac.h" | 18 #include "ui/views/widget/native_widget_mac.h" |
16 #include "ui/views/widget/widget.h" | 19 #include "ui/views/widget/widget.h" |
17 #include "ui/views/widget/widget_observer.h" | 20 #include "ui/views/widget/widget_observer.h" |
18 | 21 |
| 22 using base::ASCIIToUTF16; |
| 23 using base::SysNSStringToUTF8; |
| 24 using base::SysNSStringToUTF16; |
| 25 using base::SysUTF8ToNSString; |
| 26 |
| 27 #define EXPECT_EQ_RANGE(a, b) \ |
| 28 EXPECT_EQ(a.location, b.location); \ |
| 29 EXPECT_EQ(a.length, b.length); |
| 30 |
| 31 namespace { |
| 32 |
| 33 // Empty range shortcut for readibility. |
| 34 NSRange EmptyRange() { |
| 35 return NSMakeRange(NSNotFound, 0); |
| 36 } |
| 37 |
| 38 } // namespace |
| 39 |
19 namespace views { | 40 namespace views { |
20 namespace test { | 41 namespace test { |
21 | 42 |
22 // Provides the |parent| argument to construct a BridgedNativeWidget. | 43 // Provides the |parent| argument to construct a BridgedNativeWidget. |
23 class MockNativeWidgetMac : public NativeWidgetMac { | 44 class MockNativeWidgetMac : public NativeWidgetMac { |
24 public: | 45 public: |
25 MockNativeWidgetMac(Widget* delegate) : NativeWidgetMac(delegate) {} | 46 MockNativeWidgetMac(Widget* delegate) : NativeWidgetMac(delegate) {} |
26 | 47 |
27 // Expose a reference, so that it can be reset() independently. | 48 // Expose a reference, so that it can be reset() independently. |
28 scoped_ptr<BridgedNativeWidget>& bridge() { | 49 scoped_ptr<BridgedNativeWidget>& bridge() { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
73 protected: | 94 protected: |
74 scoped_ptr<Widget> widget_; | 95 scoped_ptr<Widget> widget_; |
75 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. | 96 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. |
76 }; | 97 }; |
77 | 98 |
78 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { | 99 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { |
79 public: | 100 public: |
80 BridgedNativeWidgetTest(); | 101 BridgedNativeWidgetTest(); |
81 virtual ~BridgedNativeWidgetTest(); | 102 virtual ~BridgedNativeWidgetTest(); |
82 | 103 |
| 104 // Install a textfield in the view hierarchy and make it the text input |
| 105 // client. |
| 106 void InstallTextField(const std::string& text); |
| 107 |
| 108 // Returns the current text as std::string. |
| 109 std::string GetText(); |
| 110 |
83 // testing::Test: | 111 // testing::Test: |
84 virtual void SetUp() OVERRIDE; | 112 virtual void SetUp() OVERRIDE; |
85 virtual void TearDown() OVERRIDE; | 113 virtual void TearDown() OVERRIDE; |
86 | 114 |
87 protected: | 115 protected: |
88 // TODO(tapted): Make this a EventCountView from widget_unittest.cc. | 116 // TODO(tapted): Make this a EventCountView from widget_unittest.cc. |
89 scoped_ptr<views::View> view_; | 117 scoped_ptr<views::View> view_; |
| 118 scoped_ptr<BridgedNativeWidget> bridge_; |
| 119 BridgedContentView* ns_view_; // Weak. Owned by bridge_. |
90 | 120 |
91 private: | 121 private: |
92 DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTest); | 122 DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTest); |
93 }; | 123 }; |
94 | 124 |
95 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { | 125 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { |
96 } | 126 } |
97 | 127 |
98 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { | 128 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { |
99 } | 129 } |
100 | 130 |
| 131 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { |
| 132 Textfield* textfield = new Textfield(); |
| 133 textfield->SetText(ASCIIToUTF16(text)); |
| 134 view_->AddChildView(textfield); |
| 135 [ns_view_ setTextInputClient:textfield]; |
| 136 } |
| 137 |
| 138 std::string BridgedNativeWidgetTest::GetText() { |
| 139 NSRange range = NSMakeRange(0, NSUIntegerMax); |
| 140 NSAttributedString* text = |
| 141 [ns_view_ attributedSubstringForProposedRange:range actualRange:NULL]; |
| 142 return SysNSStringToUTF8([text string]); |
| 143 } |
| 144 |
101 void BridgedNativeWidgetTest::SetUp() { | 145 void BridgedNativeWidgetTest::SetUp() { |
102 BridgedNativeWidgetTestBase::SetUp(); | 146 BridgedNativeWidgetTestBase::SetUp(); |
103 | 147 |
104 view_.reset(new views::View); | 148 view_.reset(new views::View); |
105 base::scoped_nsobject<NSWindow> window([test_window() retain]); | 149 base::scoped_nsobject<NSWindow> window([test_window() retain]); |
106 | 150 |
107 EXPECT_FALSE([window delegate]); | 151 EXPECT_FALSE([window delegate]); |
108 bridge()->Init(window, Widget::InitParams()); | 152 bridge()->Init(window, Widget::InitParams()); |
109 | 153 |
110 // The delegate should exist before setting the root view. | 154 // The delegate should exist before setting the root view. |
111 EXPECT_TRUE([window delegate]); | 155 EXPECT_TRUE([window delegate]); |
112 bridge()->SetRootView(view_.get()); | 156 bridge()->SetRootView(view_.get()); |
| 157 ns_view_ = bridge()->ns_view(); |
113 | 158 |
114 [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()]; | 159 [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()]; |
115 } | 160 } |
116 | 161 |
117 void BridgedNativeWidgetTest::TearDown() { | 162 void BridgedNativeWidgetTest::TearDown() { |
118 view_.reset(); | 163 view_.reset(); |
119 BridgedNativeWidgetTestBase::TearDown(); | 164 BridgedNativeWidgetTestBase::TearDown(); |
120 } | 165 } |
121 | 166 |
122 // The TEST_VIEW macro expects the view it's testing to have a superview. In | 167 // The TEST_VIEW macro expects the view it's testing to have a superview. In |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 // Make sure a resize actually occurs. | 203 // Make sure a resize actually occurs. |
159 EXPECT_NE(kTestNewWidth, view_->width()); | 204 EXPECT_NE(kTestNewWidth, view_->width()); |
160 EXPECT_NE(kTestNewHeight, view_->height()); | 205 EXPECT_NE(kTestNewHeight, view_->height()); |
161 | 206 |
162 [test_window() setFrame:NSMakeRect(0, 0, kTestNewWidth, kTestNewHeight) | 207 [test_window() setFrame:NSMakeRect(0, 0, kTestNewWidth, kTestNewHeight) |
163 display:NO]; | 208 display:NO]; |
164 EXPECT_EQ(kTestNewWidth, view_->width()); | 209 EXPECT_EQ(kTestNewWidth, view_->width()); |
165 EXPECT_EQ(kTestNewHeight, view_->height()); | 210 EXPECT_EQ(kTestNewHeight, view_->height()); |
166 } | 211 } |
167 | 212 |
168 TEST_F(BridgedNativeWidgetTest, CreateInputMethod) { | 213 TEST_F(BridgedNativeWidgetTest, CreateInputMethodShouldNotReturnNull) { |
169 scoped_ptr<views::InputMethod> input_method(bridge()->CreateInputMethod()); | 214 scoped_ptr<views::InputMethod> input_method(bridge()->CreateInputMethod()); |
170 EXPECT_TRUE(input_method); | 215 EXPECT_TRUE(input_method); |
171 } | 216 } |
172 | 217 |
173 TEST_F(BridgedNativeWidgetTest, GetHostInputMethod) { | 218 TEST_F(BridgedNativeWidgetTest, GetHostInputMethodShouldNotReturnNull) { |
174 EXPECT_TRUE(bridge()->GetHostInputMethod()); | 219 EXPECT_TRUE(bridge()->GetHostInputMethod()); |
175 } | 220 } |
176 | 221 |
177 // A simpler test harness for testing initialization flows. | 222 // A simpler test harness for testing initialization flows. |
178 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetInitTest; | 223 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetInitTest; |
179 | 224 |
180 // Test that BridgedNativeWidget remains sane if Init() is never called. | 225 // Test that BridgedNativeWidget remains sane if Init() is never called. |
181 TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) { | 226 TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) { |
182 EXPECT_FALSE(bridge()->ns_view()); | 227 EXPECT_FALSE(bridge()->ns_view()); |
183 EXPECT_FALSE(bridge()->ns_window()); | 228 EXPECT_FALSE(bridge()->ns_window()); |
(...skipping 17 matching lines...) Expand all Loading... |
201 | 246 |
202 EXPECT_FALSE([child_window parentWindow]); | 247 EXPECT_FALSE([child_window parentWindow]); |
203 bridge()->Init(child_window, params); | 248 bridge()->Init(child_window, params); |
204 | 249 |
205 EXPECT_EQ(1u, [[test_window() childWindows] count]); | 250 EXPECT_EQ(1u, [[test_window() childWindows] count]); |
206 EXPECT_EQ(test_window(), [bridge()->ns_window() parentWindow]); | 251 EXPECT_EQ(test_window(), [bridge()->ns_window() parentWindow]); |
207 bridge().reset(); | 252 bridge().reset(); |
208 EXPECT_EQ(0u, [[test_window() childWindows] count]); | 253 EXPECT_EQ(0u, [[test_window() childWindows] count]); |
209 } | 254 } |
210 | 255 |
| 256 // Test getting complete string using text input protocol. |
| 257 TEST_F(BridgedNativeWidgetTest, TextInput_GetCompleteString) { |
| 258 const std::string kTestString = "foo bar baz"; |
| 259 InstallTextField(kTestString); |
| 260 |
| 261 NSRange range = NSMakeRange(0, kTestString.size()); |
| 262 NSRange actual_range; |
| 263 NSAttributedString* text = |
| 264 [ns_view_ attributedSubstringForProposedRange:range |
| 265 actualRange:&actual_range]; |
| 266 EXPECT_EQ(kTestString, SysNSStringToUTF8([text string])); |
| 267 EXPECT_EQ_RANGE(range, actual_range); |
| 268 } |
| 269 |
| 270 // Test getting middle substring using text input protocol. |
| 271 TEST_F(BridgedNativeWidgetTest, TextInput_GetMiddleSubstring) { |
| 272 const std::string kTestString = "foo bar baz"; |
| 273 InstallTextField(kTestString); |
| 274 |
| 275 NSRange range = NSMakeRange(4, 3); |
| 276 NSRange actual_range; |
| 277 NSAttributedString* text = |
| 278 [ns_view_ attributedSubstringForProposedRange:range |
| 279 actualRange:&actual_range]; |
| 280 EXPECT_EQ("bar", SysNSStringToUTF8([text string])); |
| 281 EXPECT_EQ_RANGE(range, actual_range); |
| 282 } |
| 283 |
| 284 // Test getting ending substring using text input protocol. |
| 285 TEST_F(BridgedNativeWidgetTest, TextInput_GetEndingSubstring) { |
| 286 const std::string kTestString = "foo bar baz"; |
| 287 InstallTextField(kTestString); |
| 288 |
| 289 NSRange range = NSMakeRange(8, 100); |
| 290 NSRange actual_range; |
| 291 NSAttributedString* text = |
| 292 [ns_view_ attributedSubstringForProposedRange:range |
| 293 actualRange:&actual_range]; |
| 294 EXPECT_EQ("baz", SysNSStringToUTF8([text string])); |
| 295 EXPECT_EQ(range.location, actual_range.location); |
| 296 EXPECT_EQ(3U, actual_range.length); |
| 297 } |
| 298 |
| 299 // Test getting empty substring using text input protocol. |
| 300 TEST_F(BridgedNativeWidgetTest, TextInput_GetEmptySubstring) { |
| 301 const std::string kTestString = "foo bar baz"; |
| 302 InstallTextField(kTestString); |
| 303 |
| 304 NSRange range = EmptyRange(); |
| 305 NSRange actual_range; |
| 306 NSAttributedString* text = |
| 307 [ns_view_ attributedSubstringForProposedRange:range |
| 308 actualRange:&actual_range]; |
| 309 EXPECT_EQ("", SysNSStringToUTF8([text string])); |
| 310 EXPECT_EQ_RANGE(range, actual_range); |
| 311 } |
| 312 |
| 313 // Test inserting text using text input protocol. |
| 314 TEST_F(BridgedNativeWidgetTest, TextInput_InsertText) { |
| 315 const std::string kTestString = "foo"; |
| 316 InstallTextField(kTestString); |
| 317 |
| 318 [ns_view_ insertText:SysUTF8ToNSString(kTestString) |
| 319 replacementRange:EmptyRange()]; |
| 320 gfx::Range range(0, kTestString.size()); |
| 321 base::string16 text; |
| 322 EXPECT_TRUE([ns_view_ textInputClient]->GetTextFromRange(range, &text)); |
| 323 EXPECT_EQ(ASCIIToUTF16(kTestString), text); |
| 324 } |
| 325 |
| 326 // Test replacing text using text input protocol. |
| 327 TEST_F(BridgedNativeWidgetTest, TextInput_ReplaceText) { |
| 328 const std::string kTestString = "foo bar"; |
| 329 InstallTextField(kTestString); |
| 330 |
| 331 [ns_view_ insertText:@"baz" replacementRange:NSMakeRange(4, 3)]; |
| 332 EXPECT_EQ("foo baz", GetText()); |
| 333 } |
| 334 |
| 335 // Test IME composition using text input protocol. |
| 336 TEST_F(BridgedNativeWidgetTest, TextInput_Compose) { |
| 337 const std::string kTestString = "foo "; |
| 338 InstallTextField(kTestString); |
| 339 |
| 340 EXPECT_FALSE([ns_view_ hasMarkedText]); |
| 341 EXPECT_EQ_RANGE(EmptyRange(), [ns_view_ markedRange]); |
| 342 |
| 343 // Start composition. |
| 344 NSString* compositionText = @"bar"; |
| 345 NSUInteger compositionLength = [compositionText length]; |
| 346 [ns_view_ setMarkedText:compositionText |
| 347 selectedRange:NSMakeRange(0, 2) |
| 348 replacementRange:EmptyRange()]; |
| 349 EXPECT_TRUE([ns_view_ hasMarkedText]); |
| 350 EXPECT_EQ_RANGE(NSMakeRange(kTestString.size(), compositionLength), |
| 351 [ns_view_ markedRange]); |
| 352 EXPECT_EQ_RANGE(NSMakeRange(kTestString.size(), 2), [ns_view_ selectedRange]); |
| 353 |
| 354 // Confirm composition. |
| 355 [ns_view_ unmarkText]; |
| 356 EXPECT_FALSE([ns_view_ hasMarkedText]); |
| 357 EXPECT_EQ_RANGE(EmptyRange(), [ns_view_ markedRange]); |
| 358 EXPECT_EQ("foo bar", GetText()); |
| 359 EXPECT_EQ_RANGE(NSMakeRange(GetText().size(), 0), [ns_view_ selectedRange]); |
| 360 } |
| 361 |
| 362 // Test moving the caret left and right using text input protocol. |
| 363 TEST_F(BridgedNativeWidgetTest, TextInput_MoveLeftRight) { |
| 364 InstallTextField("foo"); |
| 365 EXPECT_EQ_RANGE(NSMakeRange(3, 0), [ns_view_ selectedRange]); |
| 366 |
| 367 // Move right not allowed, out of range. |
| 368 [ns_view_ doCommandBySelector:@selector(moveRight:)]; |
| 369 EXPECT_EQ_RANGE(NSMakeRange(3, 0), [ns_view_ selectedRange]); |
| 370 |
| 371 // Move left. |
| 372 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; |
| 373 EXPECT_EQ_RANGE(NSMakeRange(2, 0), [ns_view_ selectedRange]); |
| 374 |
| 375 // Move right. |
| 376 [ns_view_ doCommandBySelector:@selector(moveRight:)]; |
| 377 EXPECT_EQ_RANGE(NSMakeRange(3, 0), [ns_view_ selectedRange]); |
| 378 } |
| 379 |
| 380 // Test backward delete using text input protocol. |
| 381 TEST_F(BridgedNativeWidgetTest, TextInput_DeleteBackward) { |
| 382 InstallTextField("a"); |
| 383 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); |
| 384 |
| 385 // Delete one character. |
| 386 [ns_view_ doCommandBySelector:@selector(deleteBackward:)]; |
| 387 EXPECT_EQ("", GetText()); |
| 388 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); |
| 389 |
| 390 // Try to delete again on an empty string. |
| 391 [ns_view_ doCommandBySelector:@selector(deleteBackward:)]; |
| 392 EXPECT_EQ("", GetText()); |
| 393 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); |
| 394 } |
| 395 |
| 396 // Test forward delete using text input protocol. |
| 397 TEST_F(BridgedNativeWidgetTest, TextInput_DeleteForward) { |
| 398 InstallTextField("a"); |
| 399 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); |
| 400 |
| 401 // At the end of the string, can't delete forward. |
| 402 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; |
| 403 EXPECT_EQ("a", GetText()); |
| 404 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); |
| 405 |
| 406 // Should succeed after moving left first. |
| 407 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; |
| 408 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; |
| 409 EXPECT_EQ("", GetText()); |
| 410 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); |
| 411 } |
| 412 |
211 } // namespace test | 413 } // namespace test |
212 } // namespace views | 414 } // namespace views |
OLD | NEW |