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

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: Rebased 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
« no previous file with comments | « ui/views/cocoa/bridged_content_view.mm ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/macros.h" 12 #include "base/macros.h"
13 #include "base/memory/scoped_ptr.h" 13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/sys_string_conversions.h" 15 #include "base/strings/sys_string_conversions.h"
16 #include "base/strings/utf_string_conversions.h" 16 #include "base/strings/utf_string_conversions.h"
17 #import "testing/gtest_mac.h" 17 #import "testing/gtest_mac.h"
18 #import "ui/base/cocoa/window_size_constants.h" 18 #import "ui/base/cocoa/window_size_constants.h"
19 #include "ui/base/ime/input_method.h" 19 #include "ui/base/ime/input_method.h"
20 #import "ui/gfx/test/ui_cocoa_test_helper.h" 20 #import "ui/gfx/test/ui_cocoa_test_helper.h"
21 #import "ui/gfx/mac/coordinate_conversion.h"
21 #import "ui/views/cocoa/bridged_content_view.h" 22 #import "ui/views/cocoa/bridged_content_view.h"
22 #import "ui/views/cocoa/native_widget_mac_nswindow.h" 23 #import "ui/views/cocoa/native_widget_mac_nswindow.h"
23 #import "ui/views/cocoa/views_nswindow_delegate.h" 24 #import "ui/views/cocoa/views_nswindow_delegate.h"
24 #include "ui/views/controls/textfield/textfield.h" 25 #include "ui/views/controls/textfield/textfield.h"
25 #include "ui/views/view.h" 26 #include "ui/views/view.h"
26 #include "ui/views/widget/native_widget_mac.h" 27 #include "ui/views/widget/native_widget_mac.h"
27 #include "ui/views/widget/root_view.h" 28 #include "ui/views/widget/root_view.h"
28 #include "ui/views/widget/widget.h" 29 #include "ui/views/widget/widget.h"
29 #include "ui/views/widget/widget_observer.h" 30 #include "ui/views/widget/widget_observer.h"
30 31
31 using base::ASCIIToUTF16; 32 using base::ASCIIToUTF16;
32 using base::SysNSStringToUTF8; 33 using base::SysNSStringToUTF8;
33 using base::SysNSStringToUTF16; 34 using base::SysNSStringToUTF16;
34 using base::SysUTF8ToNSString; 35 using base::SysUTF8ToNSString;
35 36
36 #define EXPECT_EQ_RANGE(a, b) \ 37 #define EXPECT_EQ_RANGE(a, b) \
37 EXPECT_EQ(a.location, b.location); \ 38 EXPECT_EQ(a.location, b.location); \
38 EXPECT_EQ(a.length, b.length); 39 EXPECT_EQ(a.length, b.length);
39 40
40 namespace { 41 namespace {
41 42
42 // Empty range shortcut for readibility. 43 // Empty range shortcut for readibility.
43 NSRange EmptyRange() { 44 NSRange EmptyRange() {
44 return NSMakeRange(NSNotFound, 0); 45 return NSMakeRange(NSNotFound, 0);
45 } 46 }
46 47
48 // Sets |composition_text| as the composition text with caret placed at
49 // |caret_pos| and updates |caret_range|.
50 void SetCompositionText(ui::TextInputClient* client,
51 const base::string16& composition_text,
52 const int caret_pos,
53 NSRange* caret_range) {
54 ui::CompositionText composition;
55 composition.selection = gfx::Range(caret_pos);
56 composition.text = composition_text;
57 client->SetCompositionText(composition);
58 if (caret_range)
59 *caret_range = NSMakeRange(caret_pos, 0);
60 }
61
62 // Returns a zero width rectangle corresponding to current caret position.
63 gfx::Rect GetCaretBounds(const ui::TextInputClient* client) {
64 gfx::Rect caret_bounds = client->GetCaretBounds();
65 caret_bounds.set_width(0);
66 return caret_bounds;
67 }
68
69 // Returns a zero width rectangle corresponding to caret bounds when it's placed
70 // at |caret_pos| and updates |caret_range|.
71 gfx::Rect GetCaretBoundsForPosition(ui::TextInputClient* client,
72 const base::string16& composition_text,
73 const int caret_pos,
74 NSRange* caret_range) {
75 SetCompositionText(client, composition_text, caret_pos, caret_range);
76 return GetCaretBounds(client);
77 }
78
79 // Returns the expected boundary rectangle for characters of |composition_text|
80 // within the |query_range|.
81 gfx::Rect GetExpectedBoundsForRange(ui::TextInputClient* client,
82 const base::string16& composition_text,
83 NSRange query_range) {
84 gfx::Rect left_caret = GetCaretBoundsForPosition(
85 client, composition_text, query_range.location, nullptr);
86 gfx::Rect right_caret = GetCaretBoundsForPosition(
87 client, composition_text, query_range.location + query_range.length,
88 nullptr);
89
90 // The expected bounds correspond to the area between the left and right caret
91 // positions.
92 return gfx::Rect(left_caret.x(), left_caret.y(),
93 right_caret.x() - left_caret.x(), left_caret.height());
94 }
95
47 } // namespace 96 } // namespace
48 97
49 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates 98 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates
50 // NSWindow's behavior when attempting to toggle fullscreen state again, when 99 // NSWindow's behavior when attempting to toggle fullscreen state again, when
51 // the last attempt failed but Cocoa has not yet sent 100 // the last attempt failed but Cocoa has not yet sent
52 // windowDidFailToEnterFullScreen:. 101 // windowDidFailToEnterFullScreen:.
53 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow { 102 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow {
54 @private 103 @private
55 int ignoredToggleFullScreenCount_; 104 int ignoredToggleFullScreenCount_;
56 } 105 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 // before the tests covering the Init() flow are ready to do that. 171 // before the tests covering the Init() flow are ready to do that.
123 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; 172 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS;
124 173
125 // To control the lifetime without an actual window that must be closed, 174 // To control the lifetime without an actual window that must be closed,
126 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. 175 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET.
127 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 176 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
128 177
129 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. 178 // Opacity defaults to "infer" which is usually updated by ViewsDelegate.
130 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; 179 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW;
131 180
181 init_params_.bounds = gfx::Rect(100, 100, 100, 100);
182
132 native_widget_mac_->GetWidget()->Init(init_params_); 183 native_widget_mac_->GetWidget()->Init(init_params_);
133 } 184 }
134 185
135 protected: 186 protected:
136 scoped_ptr<Widget> widget_; 187 scoped_ptr<Widget> widget_;
137 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. 188 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|.
138 189
139 // Make the InitParams available to tests to cover initialization codepaths. 190 // Make the InitParams available to tests to cover initialization codepaths.
140 Widget::InitParams init_params_; 191 Widget::InitParams init_params_;
141 }; 192 };
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 } 229 }
179 230
180 void BridgedNativeWidgetTest::InstallTextField( 231 void BridgedNativeWidgetTest::InstallTextField(
181 const std::string& text, 232 const std::string& text,
182 ui::TextInputType text_input_type) { 233 ui::TextInputType text_input_type) {
183 Textfield* textfield = new Textfield(); 234 Textfield* textfield = new Textfield();
184 textfield->SetText(ASCIIToUTF16(text)); 235 textfield->SetText(ASCIIToUTF16(text));
185 textfield->SetTextInputType(text_input_type); 236 textfield->SetTextInputType(text_input_type);
186 view_->RemoveAllChildViews(true); 237 view_->RemoveAllChildViews(true);
187 view_->AddChildView(textfield); 238 view_->AddChildView(textfield);
239 textfield->SetBoundsRect(init_params_.bounds);
188 240
189 // Request focus so the InputMethod can dispatch events to the RootView, and 241 // Request focus so the InputMethod can dispatch events to the RootView, and
190 // have them delivered to the textfield. Note that focusing a textfield 242 // have them delivered to the textfield. Note that focusing a textfield
191 // schedules a task to flash the cursor, so this requires |message_loop_|. 243 // schedules a task to flash the cursor, so this requires |message_loop_|.
192 textfield->RequestFocus(); 244 textfield->RequestFocus();
193 245
194 [ns_view_ setTextInputClient:textfield]; 246 [ns_view_ setTextInputClient:textfield];
195 } 247 }
196 248
197 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { 249 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
526 EXPECT_EQ("a", GetText()); 578 EXPECT_EQ("a", GetText());
527 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); 579 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]);
528 580
529 // Should succeed after moving left first. 581 // Should succeed after moving left first.
530 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; 582 [ns_view_ doCommandBySelector:@selector(moveLeft:)];
531 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; 583 [ns_view_ doCommandBySelector:@selector(deleteForward:)];
532 EXPECT_EQ("", GetText()); 584 EXPECT_EQ("", GetText());
533 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); 585 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]);
534 } 586 }
535 587
588 // Test firstRectForCharacterRange:actualRange for cases where query range is
589 // empty or outside composition range.
590 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) {
591 InstallTextField("");
592 ui::TextInputClient* client = [ns_view_ textInputClient];
593
594 // No composition. Ensure bounds and range corresponding to the current caret
595 // position are returned.
596 // Initially selection range will be [0, 0].
597 NSRange caret_range = NSMakeRange(0, 0);
598 NSRange query_range = NSMakeRange(1, 1);
599 NSRange actual_range;
600 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
601 actualRange:&actual_range];
602 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
603 EXPECT_EQ_RANGE(caret_range, actual_range);
604
605 // Set composition with caret before second character ('e').
606 const base::string16 test_string = base::ASCIIToUTF16("test_str");
607 const size_t kTextLength = 8;
608 SetCompositionText(client, test_string, 1, &caret_range);
609
610 // Test bounds returned for empty ranges within composition range. As per
611 // Apple's documentation for firstRectForCharacterRange:actualRange:, for an
612 // empty query range, the returned rectangle should coincide with the
613 // insertion point and have zero width. However in our implementation, if the
614 // empty query range lies within the composition range, we return a zero width
615 // rectangle corresponding to the query range location.
616
617 // Test bounds returned for empty range before second character ('e') are same
618 // as caret bounds when placed before second character.
619 query_range = NSMakeRange(1, 0);
620 rect = [ns_view_ firstRectForCharacterRange:query_range
621 actualRange:&actual_range];
622 EXPECT_EQ(GetCaretBoundsForPosition(client, test_string, 1, &caret_range),
623 gfx::ScreenRectFromNSRect(rect));
624 EXPECT_EQ_RANGE(query_range, actual_range);
625
626 // Test bounds returned for empty range after the composition text are same as
627 // caret bounds when placed after the composition text.
628 query_range = NSMakeRange(kTextLength, 0);
629 rect = [ns_view_ firstRectForCharacterRange:query_range
630 actualRange:&actual_range];
631 EXPECT_NE(GetCaretBoundsForPosition(client, test_string, 1, &caret_range),
632 gfx::ScreenRectFromNSRect(rect));
633 EXPECT_EQ(
634 GetCaretBoundsForPosition(client, test_string, kTextLength, &caret_range),
635 gfx::ScreenRectFromNSRect(rect));
636 EXPECT_EQ_RANGE(query_range, actual_range);
637
638 // Query outside composition range. Ensure bounds and range corresponding to
639 // the current caret position are returned.
640 query_range = NSMakeRange(kTextLength + 1, 0);
641 rect = [ns_view_ firstRectForCharacterRange:query_range
642 actualRange:&actual_range];
643 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
644 EXPECT_EQ_RANGE(caret_range, actual_range);
645
646 // Make sure not crashing by passing null pointer instead of actualRange.
647 rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:nullptr];
648 }
649
650 // Test firstRectForCharacterRange:actualRange for non-empty query ranges within
651 // the composition range.
652 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) {
653 InstallTextField("");
654 ui::TextInputClient* client = [ns_view_ textInputClient];
655
656 const base::string16 kTestString = base::ASCIIToUTF16("test_str");
657 const size_t kTextLength = 8;
658 SetCompositionText(client, kTestString, 1, nullptr);
659
660 // Query bounds for the whole composition string.
661 NSRange query_range = NSMakeRange(0, kTextLength);
662 NSRange actual_range;
663 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
664 actualRange:&actual_range];
665 EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range),
666 gfx::ScreenRectFromNSRect(rect));
667 EXPECT_EQ_RANGE(query_range, actual_range);
668
669 // Query bounds for the substring "est_".
670 query_range = NSMakeRange(1, 4);
671 rect = [ns_view_ firstRectForCharacterRange:query_range
672 actualRange:&actual_range];
673 EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range),
674 gfx::ScreenRectFromNSRect(rect));
675 EXPECT_EQ_RANGE(query_range, actual_range);
676 }
677
536 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; 678 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest;
537 679
538 // Simulate the notifications that AppKit would send out if a fullscreen 680 // Simulate the notifications that AppKit would send out if a fullscreen
539 // operation begins, and then fails and must abort. This notification sequence 681 // operation begins, and then fails and must abort. This notification sequence
540 // was determined by posting delayed tasks to toggle fullscreen state and then 682 // was determined by posting delayed tasks to toggle fullscreen state and then
541 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause 683 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause
542 // the fullscreen transition to fail. 684 // the fullscreen transition to fail.
543 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { 685 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) {
544 if (base::mac::IsOSSnowLeopard()) 686 if (base::mac::IsOSSnowLeopard())
545 return; 687 return;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
604 [center postNotificationName:NSWindowDidExitFullScreenNotification 746 [center postNotificationName:NSWindowDidExitFullScreenNotification
605 object:window]; 747 object:window];
606 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. 748 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change.
607 EXPECT_FALSE(bridge()->target_fullscreen_state()); 749 EXPECT_FALSE(bridge()->target_fullscreen_state());
608 750
609 widget_->CloseNow(); 751 widget_->CloseNow();
610 } 752 }
611 753
612 } // namespace test 754 } // namespace test
613 } // namespace views 755 } // namespace views
OLDNEW
« no previous file with comments | « 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