Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: ui/views/cocoa/bridged_native_widget_unittest.mm

Issue 1907253002: MacViews: Implement move and move*AndModifySelection commands. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 <memory> 9 #include <memory>
10 10
11 #import "base/mac/foundation_util.h" 11 #import "base/mac/foundation_util.h"
12 #import "base/mac/mac_util.h" 12 #import "base/mac/mac_util.h"
13 #import "base/mac/sdk_forward_declarations.h" 13 #import "base/mac/sdk_forward_declarations.h"
14 #include "base/macros.h" 14 #include "base/macros.h"
15 #include "base/message_loop/message_loop.h" 15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/stringprintf.h"
16 #include "base/strings/sys_string_conversions.h" 17 #include "base/strings/sys_string_conversions.h"
17 #include "base/strings/utf_string_conversions.h" 18 #include "base/strings/utf_string_conversions.h"
18 #import "testing/gtest_mac.h" 19 #import "testing/gtest_mac.h"
19 #import "ui/base/cocoa/window_size_constants.h" 20 #import "ui/base/cocoa/window_size_constants.h"
20 #include "ui/base/ime/input_method.h" 21 #include "ui/base/ime/input_method.h"
21 #import "ui/gfx/mac/coordinate_conversion.h" 22 #import "ui/gfx/mac/coordinate_conversion.h"
22 #import "ui/gfx/test/ui_cocoa_test_helper.h" 23 #import "ui/gfx/test/ui_cocoa_test_helper.h"
23 #import "ui/views/cocoa/bridged_content_view.h" 24 #import "ui/views/cocoa/bridged_content_view.h"
24 #import "ui/views/cocoa/native_widget_mac_nswindow.h" 25 #import "ui/views/cocoa/native_widget_mac_nswindow.h"
25 #import "ui/views/cocoa/views_nswindow_delegate.h" 26 #import "ui/views/cocoa/views_nswindow_delegate.h"
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 Widget::InitParams init_params_; 206 Widget::InitParams init_params_;
206 }; 207 };
207 208
208 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase { 209 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase {
209 public: 210 public:
210 BridgedNativeWidgetTest(); 211 BridgedNativeWidgetTest();
211 ~BridgedNativeWidgetTest() override; 212 ~BridgedNativeWidgetTest() override;
212 213
213 // Install a textfield with input type |text_input_type| in the view hierarchy 214 // Install a textfield with input type |text_input_type| in the view hierarchy
214 // and make it the text input client. Also initializes |dummy_text_view_|. 215 // and make it the text input client. Also initializes |dummy_text_view_|.
215 void InstallTextField(const std::string& text, 216 void InstallTextField(const base::string16& text,
216 ui::TextInputType text_input_type); 217 ui::TextInputType text_input_type);
217 218
218 // Install a textfield with input type ui::TEXT_INPUT_TYPE_TEXT in the view 219 // Install a textfield with input type ui::TEXT_INPUT_TYPE_TEXT in the view
219 // hierarchy and make it the text input client. Also initializes 220 // hierarchy and make it the text input client. Also initializes
220 // |dummy_text_view_|. 221 // |dummy_text_view_|.
222 void InstallTextField(const base::string16& text);
223
221 void InstallTextField(const std::string& text); 224 void InstallTextField(const std::string& text);
222 225
223 // Returns the actual current text for |ns_view_|. 226 // Returns the actual current text for |ns_view_|.
224 NSString* GetActualText(); 227 NSString* GetActualText();
225 228
226 // Returns the expected current text from |dummy_text_view_|. 229 // Returns the expected current text from |dummy_text_view_|.
227 NSString* GetExpectedText(); 230 NSString* GetExpectedText();
228 231
229 // Returns the actual selection range for |ns_view_|. 232 // Returns the actual selection range for |ns_view_|.
230 NSRange GetActualSelectionRange(); 233 NSRange GetActualSelectionRange();
231 234
232 // Returns the expected selection range from |dummy_text_view_|. 235 // Returns the expected selection range from |dummy_text_view_|.
233 NSRange GetExpectedSelectionRange(); 236 NSRange GetExpectedSelectionRange();
234 237
235 // Set the selection range for the installed textfield and |dummy_text_view_|. 238 // Set the selection range for the installed textfield and |dummy_text_view_|.
236 void SetSelectionRange(NSRange range); 239 void SetSelectionRange(NSRange range);
237 240
238 // Perform command |sel| on |ns_view_| and |dummy_text_view_|. 241 // Perform command |sel| on |ns_view_| and |dummy_text_view_|.
239 void PerformCommand(SEL sel); 242 void PerformCommand(SEL sel);
240 243
244 // Make selection from |start| to |end| on installed textfield and
245 // |dummy_text_view_|. If |start| > |end|, extend selection to left from
246 // |start|.
247 void MakeSelection(int start, int end);
248
241 // testing::Test: 249 // testing::Test:
242 void SetUp() override; 250 void SetUp() override;
243 void TearDown() override; 251 void TearDown() override;
244 252
245 protected: 253 protected:
246 // Test delete to beginning of line or paragraph based on |sel|. |sel| can be 254 // Test delete to beginning of line or paragraph based on |sel|. |sel| can be
247 // either deleteToBeginningOfLine: or deleteToBeginningOfParagraph:. 255 // either deleteToBeginningOfLine: or deleteToBeginningOfParagraph:.
248 void TestDeleteBeginning(SEL sel); 256 void TestDeleteBeginning(SEL sel);
249 257
250 // Test delete to end of line or paragraph based on |sel|. |sel| can be 258 // Test delete to end of line or paragraph based on |sel|. |sel| can be
(...skipping 13 matching lines...) Expand all
264 private: 272 private:
265 DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTest); 273 DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTest);
266 }; 274 };
267 275
268 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { 276 BridgedNativeWidgetTest::BridgedNativeWidgetTest() {
269 } 277 }
270 278
271 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { 279 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
272 } 280 }
273 281
282 void BridgedNativeWidgetTest::InstallTextField(const base::string16& text) {
283 InstallTextField(text, ui::TEXT_INPUT_TYPE_TEXT);
284 }
285
286 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
287 InstallTextField(base::ASCIIToUTF16(text), ui::TEXT_INPUT_TYPE_TEXT);
288 }
289
274 void BridgedNativeWidgetTest::InstallTextField( 290 void BridgedNativeWidgetTest::InstallTextField(
275 const std::string& text, 291 const base::string16& text,
276 ui::TextInputType text_input_type) { 292 ui::TextInputType text_input_type) {
277 Textfield* textfield = new Textfield(); 293 Textfield* textfield = new Textfield();
278 textfield->SetText(ASCIIToUTF16(text)); 294 textfield->SetText(text);
279 textfield->SetTextInputType(text_input_type); 295 textfield->SetTextInputType(text_input_type);
280 view_->RemoveAllChildViews(true); 296 view_->RemoveAllChildViews(true);
281 view_->AddChildView(textfield); 297 view_->AddChildView(textfield);
282 textfield->SetBoundsRect(init_params_.bounds); 298 textfield->SetBoundsRect(init_params_.bounds);
283 299
284 // Request focus so the InputMethod can dispatch events to the RootView, and 300 // Request focus so the InputMethod can dispatch events to the RootView, and
285 // have them delivered to the textfield. Note that focusing a textfield 301 // have them delivered to the textfield. Note that focusing a textfield
286 // schedules a task to flash the cursor, so this requires |message_loop_|. 302 // schedules a task to flash the cursor, so this requires |message_loop_|.
287 textfield->RequestFocus(); 303 textfield->RequestFocus();
288 304
289 [ns_view_ setTextInputClient:textfield]; 305 [ns_view_ setTextInputClient:textfield];
290 306
291 // Initialize the dummy text view. 307 // Initialize the dummy text view.
292 dummy_text_view_.reset([[NSTextView alloc] initWithFrame:NSZeroRect]); 308 dummy_text_view_.reset([[NSTextView alloc] initWithFrame:NSZeroRect]);
293 [dummy_text_view_ setString:SysUTF8ToNSString(text)]; 309 [dummy_text_view_ setString:SysUTF16ToNSString(text)];
294 }
295
296 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
297 InstallTextField(text, ui::TEXT_INPUT_TYPE_TEXT);
298 } 310 }
299 311
300 NSString* BridgedNativeWidgetTest::GetActualText() { 312 NSString* BridgedNativeWidgetTest::GetActualText() {
301 NSRange range = NSMakeRange(0, NSUIntegerMax); 313 NSRange range = NSMakeRange(0, NSUIntegerMax);
302 return [[ns_view_ attributedSubstringForProposedRange:range 314 return [[ns_view_ attributedSubstringForProposedRange:range
303 actualRange:nullptr] string]; 315 actualRange:nullptr] string];
304 } 316 }
305 317
306 NSString* BridgedNativeWidgetTest::GetExpectedText() { 318 NSString* BridgedNativeWidgetTest::GetExpectedText() {
307 NSRange range = NSMakeRange(0, NSUIntegerMax); 319 NSRange range = NSMakeRange(0, NSUIntegerMax);
(...skipping 15 matching lines...) Expand all
323 client->SetSelectionRange(gfx::Range(range)); 335 client->SetSelectionRange(gfx::Range(range));
324 336
325 [dummy_text_view_ setSelectedRange:range]; 337 [dummy_text_view_ setSelectedRange:range];
326 } 338 }
327 339
328 void BridgedNativeWidgetTest::PerformCommand(SEL sel) { 340 void BridgedNativeWidgetTest::PerformCommand(SEL sel) {
329 [ns_view_ doCommandBySelector:sel]; 341 [ns_view_ doCommandBySelector:sel];
330 [dummy_text_view_ doCommandBySelector:sel]; 342 [dummy_text_view_ doCommandBySelector:sel];
331 } 343 }
332 344
345 void BridgedNativeWidgetTest::MakeSelection(int start, int end) {
346 ui::TextInputClient* client = [ns_view_ textInputClient];
347 client->SetSelectionRange(gfx::Range(start, end));
348
349 // NSTextView does not support specifying the selection "direction" i.e. the
350 // leading edge of selection. Hence we extend the selection from |start| to
351 // |end|.
352 [dummy_text_view_ setSelectedRange:NSMakeRange(start, 0)];
353 SEL sel = start > end ? @selector(moveBackwardAndModifySelection:)
354 : @selector(moveForwardAndModifySelection:);
355 size_t delta = std::abs(end - start);
356
357 for (size_t i = 0; i < delta; i++)
358 [dummy_text_view_ doCommandBySelector:sel];
359 }
360
333 void BridgedNativeWidgetTest::SetUp() { 361 void BridgedNativeWidgetTest::SetUp() {
334 BridgedNativeWidgetTestBase::SetUp(); 362 BridgedNativeWidgetTestBase::SetUp();
335 363
336 view_.reset(new views::internal::RootView(widget_.get())); 364 view_.reset(new views::internal::RootView(widget_.get()));
337 base::scoped_nsobject<NSWindow> window([test_window() retain]); 365 base::scoped_nsobject<NSWindow> window([test_window() retain]);
338 366
339 // BridgedNativeWidget expects to be initialized with a hidden (deferred) 367 // BridgedNativeWidget expects to be initialized with a hidden (deferred)
340 // window. 368 // window.
341 [window orderOut:nil]; 369 [window orderOut:nil];
342 EXPECT_FALSE([window delegate]); 370 EXPECT_FALSE([window delegate]);
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 PerformInit(); 571 PerformInit();
544 EXPECT_TRUE([window_ hasShadow]); // Preserves shadow. 572 EXPECT_TRUE([window_ hasShadow]); // Preserves shadow.
545 573
546 window_.reset(); 574 window_.reset();
547 widget_.reset(); 575 widget_.reset();
548 } 576 }
549 577
550 // Ensure a nil NSTextInputContext is returned when the ui::TextInputClient is 578 // Ensure a nil NSTextInputContext is returned when the ui::TextInputClient is
551 // not editable, a password field, or unset. 579 // not editable, a password field, or unset.
552 TEST_F(BridgedNativeWidgetTest, InputContext) { 580 TEST_F(BridgedNativeWidgetTest, InputContext) {
553 const std::string test_string = "test_str"; 581 const base::string16 test_string = base::ASCIIToUTF16("test_str");
554 InstallTextField(test_string, ui::TEXT_INPUT_TYPE_PASSWORD); 582 InstallTextField(test_string, ui::TEXT_INPUT_TYPE_PASSWORD);
555 EXPECT_FALSE([ns_view_ inputContext]); 583 EXPECT_FALSE([ns_view_ inputContext]);
556 InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT); 584 InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT);
557 EXPECT_TRUE([ns_view_ inputContext]); 585 EXPECT_TRUE([ns_view_ inputContext]);
558 [ns_view_ setTextInputClient:nil]; 586 [ns_view_ setTextInputClient:nil];
559 EXPECT_FALSE([ns_view_ inputContext]); 587 EXPECT_FALSE([ns_view_ inputContext]);
560 InstallTextField(test_string, ui::TEXT_INPUT_TYPE_NONE); 588 InstallTextField(test_string, ui::TEXT_INPUT_TYPE_NONE);
561 EXPECT_FALSE([ns_view_ inputContext]); 589 EXPECT_FALSE([ns_view_ inputContext]);
562 } 590 }
563 591
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 } 894 }
867 895
868 TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToBeginningOfParagraph) { 896 TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToBeginningOfParagraph) {
869 TestDeleteBeginning(@selector(deleteToBeginningOfParagraph:)); 897 TestDeleteBeginning(@selector(deleteToBeginningOfParagraph:));
870 } 898 }
871 899
872 TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToEndOfParagraph) { 900 TEST_F(BridgedNativeWidgetTest, TextInput_DeleteToEndOfParagraph) {
873 TestDeleteEnd(@selector(deleteToEndOfParagraph:)); 901 TestDeleteEnd(@selector(deleteToEndOfParagraph:));
874 } 902 }
875 903
904 TEST_F(BridgedNativeWidgetTest, EditingCommands) {
905 NSArray* selectors = @[
906 @"moveForward:", @"moveRight:", @"moveBackward:", @"moveLeft:", @"moveUp:",
907 @"moveDown:", @"moveWordForward:", @"moveWordBackward:",
908 @"moveToBeginningOfLine:", @"moveToEndOfLine:",
909 @"moveToBeginningOfParagraph:", @"moveToEndOfParagraph:",
910 @"moveToEndOfDocument:", @"moveToBeginningOfDocument:", @"pageDown:",
911 @"pageUp:", @"moveBackwardAndModifySelection:",
912 @"moveForwardAndModifySelection:",
913 // @"moveWordForwardAndModifySelection:",
914 // @"moveWordBackwardAndModifySelection:",
915 @"moveUpAndModifySelection:", @"moveDownAndModifySelection:",
916 // @"moveToBeginningOfLineAndModifySelection:",
917 // @"moveToEndOfLineAndModifySelection:",
918 // @"moveToBeginningOfParagraphAndModifySelection:",
919 // @"moveToEndOfParagraphAndModifySelection:",
920 // @"moveToEndOfDocumentAndModifySelection:",
921 // @"moveToBeginningOfDocumentAndModifySelection:",
922 @"pageDownAndModifySelection:", @"pageUpAndModifySelection:",
923 // @"moveParagraphForwardAndModifySelection:",
924 // @"moveParagraphBackwardAndModifySelection:",
925 @"moveWordRight:", @"moveWordLeft:", @"moveRightAndModifySelection:",
926 @"moveLeftAndModifySelection:",
927 // @"moveWordRightAndModifySelection:",
928 // @"moveWordLeftAndModifySelection:",
929 @"moveToLeftEndOfLine:", @"moveToRightEndOfLine:",
930 // @"moveToLeftEndOfLineAndModifySelection:",
931 // @"moveToRightEndOfLineAndModifySelection:",
932 @"deleteForward:", @"deleteBackward:", @"deleteWordForward:",
933 @"deleteWordBackward:", @"deleteToBeginningOfLine:", @"deleteToEndOfLine:",
934 @"deleteToBeginningOfParagraph:", @"deleteToEndOfParagraph:",
935 @"cancelOperation:"
936 ];
937
938 struct {
939 const std::wstring test_string;
940 bool rtl;
941 } cases[] =
942 {{L"abc def", false},
943 {L"\x0634\x0632\x0630 \x064A\x062B\x0628", true}};
944
945 for (auto test_case : cases) {
946 for (NSString* selector_string in selectors) {
947 SEL sel = NSSelectorFromString(selector_string);
948 const int len = test_case.test_string.length();
949 for (int i = 0; i <= len; i++) {
950 for (int j = 0; j <= len; j++) {
951 SCOPED_TRACE(base::StringPrintf(
952 "Testing range [%d-%d] for case %s and selector %s\n", i, j,
953 base::WideToUTF8(test_case.test_string).c_str(),
954 base::SysNSStringToUTF8(selector_string).c_str()));
955
956 InstallTextField(base::WideToUTF16(test_case.test_string));
957 MakeSelection(i, j);
958 EXPECT_EQ_RANGE_3(NSMakeRange(std::min(i, j), std::abs(i - j)),
959 GetExpectedSelectionRange(),
960 GetActualSelectionRange());
961
962 PerformCommand(sel);
963 EXPECT_NSEQ(GetExpectedText(), GetActualText());
964
965 // This is another case where NSTextView behaves a bit weirdly. Turns
966 // out for RTL text, move[Left/Right]AndModifySelection and
967 // move[Forward/Backward]AndModifySelection do not share selection
968 // "direction". For eg say current rtl text is "abc|". On doing
969 // moveForwardAndModifySelection: twice, text becomes "a|bc|". Now if
970 // we do moveLeftAndModifySelection, text should become "|abc|" but
971 // instead it becomes "a|b|c". Blink also behaves similarly to our
972 // implementation in this case.
973 if (!(test_case.rtl &&
974 ([selector_string
975 isEqualToString:@"moveRightAndModifySelection:"] ||
976 [selector_string
977 isEqualToString:@"moveLeftAndModifySelection:"]))) {
978 EXPECT_EQ_RANGE(GetExpectedSelectionRange(),
979 GetActualSelectionRange());
980 }
981 }
982 }
983 }
984 }
985 }
986
876 // Test firstRectForCharacterRange:actualRange for cases where query range is 987 // Test firstRectForCharacterRange:actualRange for cases where query range is
877 // empty or outside composition range. 988 // empty or outside composition range.
878 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) { 989 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) {
879 InstallTextField(""); 990 InstallTextField("");
880 ui::TextInputClient* client = [ns_view_ textInputClient]; 991 ui::TextInputClient* client = [ns_view_ textInputClient];
881 992
882 // No composition. Ensure bounds and range corresponding to the current caret 993 // No composition. Ensure bounds and range corresponding to the current caret
883 // position are returned. 994 // position are returned.
884 // Initially selection range will be [0, 0]. 995 // Initially selection range will be [0, 0].
885 NSRange caret_range = NSMakeRange(0, 0); 996 NSRange caret_range = NSMakeRange(0, 0);
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
1030 [center postNotificationName:NSWindowDidExitFullScreenNotification 1141 [center postNotificationName:NSWindowDidExitFullScreenNotification
1031 object:window]; 1142 object:window];
1032 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. 1143 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change.
1033 EXPECT_FALSE(bridge()->target_fullscreen_state()); 1144 EXPECT_FALSE(bridge()->target_fullscreen_state());
1034 1145
1035 widget_->CloseNow(); 1146 widget_->CloseNow();
1036 } 1147 }
1037 1148
1038 } // namespace test 1149 } // namespace test
1039 } // namespace views 1150 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698