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

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

Issue 1531213002: Mac: Implement firstRectForCharacterRange:actualRange in BridgedContentView. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed review comments and made tests concise. Created 4 years, 11 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 #import "base/mac/foundation_util.h" 9 #import "base/mac/foundation_util.h"
10 #import "base/mac/mac_util.h" 10 #import "base/mac/mac_util.h"
11 #import "base/mac/sdk_forward_declarations.h" 11 #import "base/mac/sdk_forward_declarations.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h" 13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/sys_string_conversions.h" 14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #import "testing/gtest_mac.h" 16 #import "testing/gtest_mac.h"
17 #import "ui/base/cocoa/window_size_constants.h" 17 #import "ui/base/cocoa/window_size_constants.h"
18 #include "ui/base/ime/input_method.h" 18 #include "ui/base/ime/input_method.h"
19 #import "ui/gfx/test/ui_cocoa_test_helper.h" 19 #import "ui/gfx/test/ui_cocoa_test_helper.h"
20 #include "ui/gfx/mac/coordinate_conversion.h"
tapted 2015/12/30 06:48:53 nit: import
karandeepb 2015/12/31 02:42:43 Done.
20 #import "ui/views/cocoa/bridged_content_view.h" 21 #import "ui/views/cocoa/bridged_content_view.h"
21 #import "ui/views/cocoa/native_widget_mac_nswindow.h" 22 #import "ui/views/cocoa/native_widget_mac_nswindow.h"
22 #import "ui/views/cocoa/views_nswindow_delegate.h" 23 #import "ui/views/cocoa/views_nswindow_delegate.h"
23 #include "ui/views/controls/textfield/textfield.h" 24 #include "ui/views/controls/textfield/textfield.h"
24 #include "ui/views/view.h" 25 #include "ui/views/view.h"
25 #include "ui/views/widget/native_widget_mac.h" 26 #include "ui/views/widget/native_widget_mac.h"
26 #include "ui/views/widget/root_view.h" 27 #include "ui/views/widget/root_view.h"
27 #include "ui/views/widget/widget.h" 28 #include "ui/views/widget/widget.h"
28 #include "ui/views/widget/widget_observer.h" 29 #include "ui/views/widget/widget_observer.h"
29 30
30 using base::ASCIIToUTF16; 31 using base::ASCIIToUTF16;
31 using base::SysNSStringToUTF8; 32 using base::SysNSStringToUTF8;
32 using base::SysNSStringToUTF16; 33 using base::SysNSStringToUTF16;
33 using base::SysUTF8ToNSString; 34 using base::SysUTF8ToNSString;
34 35
35 #define EXPECT_EQ_RANGE(a, b) \ 36 #define EXPECT_EQ_RANGE(a, b) \
36 EXPECT_EQ(a.location, b.location); \ 37 EXPECT_EQ(a.location, b.location); \
37 EXPECT_EQ(a.length, b.length); 38 EXPECT_EQ(a.length, b.length);
38 39
39 namespace { 40 namespace {
40 41
41 // Empty range shortcut for readibility. 42 // Empty range shortcut for readibility.
42 NSRange EmptyRange() { 43 NSRange EmptyRange() {
43 return NSMakeRange(NSNotFound, 0); 44 return NSMakeRange(NSNotFound, 0);
44 } 45 }
45 46
47 // Sets |composition_text| as the composition text with caret placed at
48 // |caret_pos| and updates |caret_range|.
49 void SetCompositionText(ui::TextInputClient* client,
50 const base::string16& composition_text,
51 const int caret_pos,
52 NSRange* caret_range = nil) {
tapted 2015/12/30 06:48:53 I don't think anything relies on the default argum
karandeepb 2015/12/31 02:42:43 Done.
53 ui::CompositionText composition;
54 composition.selection = gfx::Range(caret_pos);
55 composition.text = composition_text;
56 client->SetCompositionText(composition);
57 if (caret_range)
58 *caret_range = NSMakeRange(caret_pos, 0);
59 }
60
61 // Returns a zero width rectangle corresponding to current caret position.
62 gfx::Rect GetCaretBounds(const ui::TextInputClient* client) {
63 gfx::Rect caret_bounds = client->GetCaretBounds();
64 caret_bounds.set_width(0);
65 return caret_bounds;
66 }
67
68 // Returns a zero width rectangle corresponding to caret bounds when it's placed
69 // at |caret_pos| and updates |caret_range|.
70 gfx::Rect GetCaretBoundsForPosition(ui::TextInputClient* client,
71 const base::string16& composition_text,
72 const int caret_pos,
73 NSRange* caret_range = nil) {
tapted 2015/12/30 06:48:52 I think there's one call that wants the default -
karandeepb 2015/12/31 02:42:44 Done.
74 SetCompositionText(client, composition_text, caret_pos, caret_range);
75 return GetCaretBounds(client);
76 }
77
46 } // namespace 78 } // namespace
47 79
48 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates 80 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates
49 // NSWindow's behavior when attempting to toggle fullscreen state again, when 81 // NSWindow's behavior when attempting to toggle fullscreen state again, when
50 // the last attempt failed but Cocoa has not yet sent 82 // the last attempt failed but Cocoa has not yet sent
51 // windowDidFailToEnterFullScreen:. 83 // windowDidFailToEnterFullScreen:.
52 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow { 84 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow {
53 @private 85 @private
54 int ignoredToggleFullScreenCount_; 86 int ignoredToggleFullScreenCount_;
55 } 87 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 // before the tests covering the Init() flow are ready to do that. 153 // before the tests covering the Init() flow are ready to do that.
122 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; 154 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS;
123 155
124 // To control the lifetime without an actual window that must be closed, 156 // To control the lifetime without an actual window that must be closed,
125 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. 157 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET.
126 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 158 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
127 159
128 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. 160 // Opacity defaults to "infer" which is usually updated by ViewsDelegate.
129 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; 161 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW;
130 162
163 init_params_.bounds = gfx::Rect(100, 100, 100, 100);
164
131 native_widget_mac_->GetWidget()->Init(init_params_); 165 native_widget_mac_->GetWidget()->Init(init_params_);
132 } 166 }
133 167
134 protected: 168 protected:
135 scoped_ptr<Widget> widget_; 169 scoped_ptr<Widget> widget_;
136 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. 170 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|.
137 171
138 // Make the InitParams available to tests to cover initialization codepaths. 172 // Make the InitParams available to tests to cover initialization codepaths.
139 Widget::InitParams init_params_; 173 Widget::InitParams init_params_;
140 }; 174 };
(...skipping 26 matching lines...) Expand all
167 201
168 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { 202 BridgedNativeWidgetTest::BridgedNativeWidgetTest() {
169 } 203 }
170 204
171 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { 205 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
172 } 206 }
173 207
174 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { 208 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
175 Textfield* textfield = new Textfield(); 209 Textfield* textfield = new Textfield();
176 textfield->SetText(ASCIIToUTF16(text)); 210 textfield->SetText(ASCIIToUTF16(text));
211 textfield->SetBoundsRect(init_params_.bounds);
177 view_->AddChildView(textfield); 212 view_->AddChildView(textfield);
178 213
179 // Request focus so the InputMethod can dispatch events to the RootView, and 214 // Request focus so the InputMethod can dispatch events to the RootView, and
180 // have them delivered to the textfield. Note that focusing a textfield 215 // have them delivered to the textfield. Note that focusing a textfield
181 // schedules a task to flash the cursor, so this requires |message_loop_|. 216 // schedules a task to flash the cursor, so this requires |message_loop_|.
182 textfield->RequestFocus(); 217 textfield->RequestFocus();
183 218
184 [ns_view_ setTextInputClient:textfield]; 219 [ns_view_ setTextInputClient:textfield];
185 } 220 }
186 221
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 EXPECT_EQ("a", GetText()); 533 EXPECT_EQ("a", GetText());
499 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); 534 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]);
500 535
501 // Should succeed after moving left first. 536 // Should succeed after moving left first.
502 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; 537 [ns_view_ doCommandBySelector:@selector(moveLeft:)];
503 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; 538 [ns_view_ doCommandBySelector:@selector(deleteForward:)];
504 EXPECT_EQ("", GetText()); 539 EXPECT_EQ("", GetText());
505 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); 540 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]);
506 } 541 }
507 542
543 // Test firstRectForCharacterRange:actualRange for cases where query range is
544 // empty or outside composition range.
545 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) {
546 InstallTextField("");
547 ui::TextInputClient* client = [ns_view_ textInputClient];
548
549 // No composition. Ensure bounds and range corresponding to the current caret
550 // position are returned.
551 // Initially selection range will be [0, 0].
552 NSRange caret_range = NSMakeRange(0, 0);
553 NSRange query_range = NSMakeRange(1, 1);
554 NSRange actual_range;
555 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
556 actualRange:&actual_range];
557 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
558 EXPECT_EQ_RANGE(caret_range, actual_range);
559
560 // Set composition with caret before second character ('e').
561 const base::string16 kTestString = base::ASCIIToUTF16("test_str");
tapted 2015/12/30 06:48:53 So there's a weird convention (or, the literal int
karandeepb 2015/12/31 02:42:44 Can't find this in the cpp style guide - https://e
tapted 2016/01/04 02:28:10 It says "Variables declared constexpr or const, an
karandeepb 2016/01/04 04:33:55 Done. Will refactor the other usages in a separate
562 const size_t kTextLength = 8;
563 SetCompositionText(client, kTestString, 1, &caret_range);
564
565 // Test empty ranges within the composition range.
tapted 2015/12/30 06:48:53 nit: follow comment with the expectations, E.g.
karandeepb 2015/12/31 02:42:43 Done.
566 query_range = NSMakeRange(1, 0);
567 rect = [ns_view_ firstRectForCharacterRange:query_range
568 actualRange:&actual_range];
569 EXPECT_EQ(GetCaretBoundsForPosition(client, kTestString, 1, &caret_range),
570 gfx::ScreenRectFromNSRect(rect));
571 EXPECT_EQ_RANGE(query_range, actual_range);
572
573 query_range = NSMakeRange(kTextLength, 0);
tapted 2015/12/30 06:48:53 likewise, "Test empty range at the end of the text
karandeepb 2015/12/31 02:42:44 Done.
574 rect = [ns_view_ firstRectForCharacterRange:query_range
575 actualRange:&actual_range];
576 EXPECT_NE(GetCaretBoundsForPosition(client, kTestString, 1, &caret_range),
577 gfx::ScreenRectFromNSRect(rect));
578 EXPECT_EQ(
579 GetCaretBoundsForPosition(client, kTestString, kTextLength, &caret_range),
580 gfx::ScreenRectFromNSRect(rect));
581 EXPECT_EQ_RANGE(query_range, actual_range);
582
583 // Query outside composition range. Ensure bounds and range corresponding to
584 // the current caret position are returned.
585 query_range = NSMakeRange(kTextLength + 1, 0);
586 rect = [ns_view_ firstRectForCharacterRange:query_range
587 actualRange:&actual_range];
588 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
589 EXPECT_EQ_RANGE(caret_range, actual_range);
590
591 // Make sure not crashing by passing NULL pointer instead of actualRange.
tapted 2015/12/30 06:48:52 nit: NULL->null
karandeepb 2015/12/31 02:42:43 Done.
592 rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:NULL];
tapted 2015/12/30 06:48:53 nit: NULL->nullptr (again it's convention: `null`
karandeepb 2015/12/31 02:42:43 Done.
tapted 2016/01/04 02:28:10 (ooh, I should say -- and you may have already fig
karandeepb 2016/01/04 04:33:55 Didn't know all of it. Thanks!
593 }
594
595 // Test firstRectForCharacterRange:actualRange for all valid ranges of a test
596 // string.
597 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) {
598 InstallTextField("");
599 ui::TextInputClient* client = [ns_view_ textInputClient];
600 const base::string16 kTestString = base::ASCIIToUTF16("test_str");
tapted 2015/12/30 06:48:53 Is there a minimum test string that can give code
karandeepb 2015/12/31 02:42:43 Removed the loops. Somehow, this approach did not
601 const size_t kTextLength = 8;
602
603 std::vector<gfx::Rect> char_bounds(kTextLength);
604 std::vector<gfx::Rect> caret_bounds(kTextLength + 1);
605
606 // Generate caret_bounds between different characters.
607 for (size_t i = 0; i <= kTextLength; i++)
608 caret_bounds[i] = GetCaretBoundsForPosition(client, kTestString, i);
609
610 // Generate individual character bounds from caret positions.
611 for (size_t i = 0; i < kTextLength; i++) {
612 char_bounds[i].set_origin(caret_bounds[i].origin());
613 char_bounds[i].set_width(caret_bounds[i + 1].x() - caret_bounds[i].x());
614 char_bounds[i].set_height(caret_bounds[i].height());
615 }
616
617 // Verify bounds for all valid ranges.
618 NSRange query_range;
619 NSRange actual_range;
620 NSRect rect;
621 gfx::Rect expected_bounds;
622 for (size_t i = 0; i < kTextLength; i++) {
623 for (size_t j = i + 1; j <= kTextLength; j++) {
624 query_range = NSMakeRange(i, j - i);
625 rect = [ns_view_ firstRectForCharacterRange:query_range
626 actualRange:&actual_range];
627
628 expected_bounds = gfx::Rect();
629 for (size_t k = i; k < j; k++)
630 expected_bounds.Union(char_bounds[k]);
631
632 EXPECT_EQ(expected_bounds, gfx::ScreenRectFromNSRect(rect));
633 EXPECT_EQ_RANGE(query_range, actual_range);
634 }
635 }
636 }
637
508 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; 638 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest;
509 639
510 // Simulate the notifications that AppKit would send out if a fullscreen 640 // Simulate the notifications that AppKit would send out if a fullscreen
511 // operation begins, and then fails and must abort. This notification sequence 641 // operation begins, and then fails and must abort. This notification sequence
512 // was determined by posting delayed tasks to toggle fullscreen state and then 642 // was determined by posting delayed tasks to toggle fullscreen state and then
513 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause 643 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause
514 // the fullscreen transition to fail. 644 // the fullscreen transition to fail.
515 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { 645 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) {
516 if (base::mac::IsOSSnowLeopard()) 646 if (base::mac::IsOSSnowLeopard())
517 return; 647 return;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 [center postNotificationName:NSWindowDidExitFullScreenNotification 706 [center postNotificationName:NSWindowDidExitFullScreenNotification
577 object:window]; 707 object:window];
578 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. 708 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change.
579 EXPECT_FALSE(bridge()->target_fullscreen_state()); 709 EXPECT_FALSE(bridge()->target_fullscreen_state());
580 710
581 widget_->CloseNow(); 711 widget_->CloseNow();
582 } 712 }
583 713
584 } // namespace test 714 } // namespace test
585 } // namespace views 715 } // namespace views
OLDNEW
« ui/views/cocoa/bridged_content_view.mm ('K') | « ui/views/cocoa/bridged_content_view.mm ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698