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

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 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 #import "ui/gfx/mac/coordinate_conversion.h"
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) {
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) {
74 SetCompositionText(client, composition_text, caret_pos, caret_range);
75 return GetCaretBounds(client);
76 }
77
78 // Returns the expected boundary rectangle for characters of |composition_text|
79 // within the |query_range|.
80 gfx::Rect GetExpectedBoundsForRange(ui::TextInputClient* client,
81 const base::string16& composition_text,
82 NSRange query_range) {
83 gfx::Rect left_caret = GetCaretBoundsForPosition(
84 client, composition_text, query_range.location, nullptr);
85 gfx::Rect right_caret = GetCaretBoundsForPosition(
86 client, composition_text, query_range.location + query_range.length,
87 nullptr);
88
89 // The expected bounds correspond to the area between the left and right caret
90 // positions.
91 return gfx::Rect(left_caret.x(), left_caret.y(),
92 right_caret.x() - left_caret.x(), left_caret.height());
93 }
94
46 } // namespace 95 } // namespace
47 96
48 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates 97 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates
49 // NSWindow's behavior when attempting to toggle fullscreen state again, when 98 // NSWindow's behavior when attempting to toggle fullscreen state again, when
50 // the last attempt failed but Cocoa has not yet sent 99 // the last attempt failed but Cocoa has not yet sent
51 // windowDidFailToEnterFullScreen:. 100 // windowDidFailToEnterFullScreen:.
52 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow { 101 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow {
53 @private 102 @private
54 int ignoredToggleFullScreenCount_; 103 int ignoredToggleFullScreenCount_;
55 } 104 }
(...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. 170 // before the tests covering the Init() flow are ready to do that.
122 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; 171 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS;
123 172
124 // To control the lifetime without an actual window that must be closed, 173 // To control the lifetime without an actual window that must be closed,
125 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. 174 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET.
126 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 175 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
127 176
128 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. 177 // Opacity defaults to "infer" which is usually updated by ViewsDelegate.
129 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; 178 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW;
130 179
180 init_params_.bounds = gfx::Rect(100, 100, 100, 100);
181
131 native_widget_mac_->GetWidget()->Init(init_params_); 182 native_widget_mac_->GetWidget()->Init(init_params_);
132 } 183 }
133 184
134 protected: 185 protected:
135 scoped_ptr<Widget> widget_; 186 scoped_ptr<Widget> widget_;
136 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. 187 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|.
137 188
138 // Make the InitParams available to tests to cover initialization codepaths. 189 // Make the InitParams available to tests to cover initialization codepaths.
139 Widget::InitParams init_params_; 190 Widget::InitParams init_params_;
140 }; 191 };
(...skipping 26 matching lines...) Expand all
167 218
168 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { 219 BridgedNativeWidgetTest::BridgedNativeWidgetTest() {
169 } 220 }
170 221
171 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { 222 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
172 } 223 }
173 224
174 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { 225 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
175 Textfield* textfield = new Textfield(); 226 Textfield* textfield = new Textfield();
176 textfield->SetText(ASCIIToUTF16(text)); 227 textfield->SetText(ASCIIToUTF16(text));
228 textfield->SetBoundsRect(init_params_.bounds);
177 view_->AddChildView(textfield); 229 view_->AddChildView(textfield);
178 230
179 // Request focus so the InputMethod can dispatch events to the RootView, and 231 // Request focus so the InputMethod can dispatch events to the RootView, and
180 // have them delivered to the textfield. Note that focusing a textfield 232 // have them delivered to the textfield. Note that focusing a textfield
181 // schedules a task to flash the cursor, so this requires |message_loop_|. 233 // schedules a task to flash the cursor, so this requires |message_loop_|.
182 textfield->RequestFocus(); 234 textfield->RequestFocus();
183 235
184 [ns_view_ setTextInputClient:textfield]; 236 [ns_view_ setTextInputClient:textfield];
185 } 237 }
186 238
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 EXPECT_EQ("a", GetText()); 550 EXPECT_EQ("a", GetText());
499 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); 551 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]);
500 552
501 // Should succeed after moving left first. 553 // Should succeed after moving left first.
502 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; 554 [ns_view_ doCommandBySelector:@selector(moveLeft:)];
503 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; 555 [ns_view_ doCommandBySelector:@selector(deleteForward:)];
504 EXPECT_EQ("", GetText()); 556 EXPECT_EQ("", GetText());
505 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); 557 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]);
506 } 558 }
507 559
560 // Test firstRectForCharacterRange:actualRange for cases where query range is
561 // empty or outside composition range.
562 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) {
563 InstallTextField("");
564 ui::TextInputClient* client = [ns_view_ textInputClient];
565
566 // No composition. Ensure bounds and range corresponding to the current caret
567 // position are returned.
568 // Initially selection range will be [0, 0].
569 NSRange caret_range = NSMakeRange(0, 0);
570 NSRange query_range = NSMakeRange(1, 1);
571 NSRange actual_range;
572 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
573 actualRange:&actual_range];
574 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
575 EXPECT_EQ_RANGE(caret_range, actual_range);
576
577 // Set composition with caret before second character ('e').
578 const base::string16 test_string = base::ASCIIToUTF16("test_str");
579 const size_t kTextLength = 8;
580 SetCompositionText(client, test_string, 1, &caret_range);
581
582 // Test bounds returned for empty ranges within composition range. As per
583 // Apple's documentation for firstRectForCharacterRange:actualRange:, for an
584 // empty query range, the returned rectangle should coincide with the
585 // insertion point and have zero width. However in our implementation, if the
586 // empty query range lies within the composition range, we return a zero width
587 // rectangle corresponding to the query range location.
588
589 // Test bounds returned for empty range before second character ('e') are same
590 // as caret bounds when placed before second character.
591 query_range = NSMakeRange(1, 0);
592 rect = [ns_view_ firstRectForCharacterRange:query_range
593 actualRange:&actual_range];
594 EXPECT_EQ(GetCaretBoundsForPosition(client, test_string, 1, &caret_range),
595 gfx::ScreenRectFromNSRect(rect));
596 EXPECT_EQ_RANGE(query_range, actual_range);
597
598 // Test bounds returned for empty range after the composition text are same as
599 // caret bounds when placed after the composition text.
600 query_range = NSMakeRange(kTextLength, 0);
601 rect = [ns_view_ firstRectForCharacterRange:query_range
602 actualRange:&actual_range];
603 EXPECT_NE(GetCaretBoundsForPosition(client, test_string, 1, &caret_range),
604 gfx::ScreenRectFromNSRect(rect));
605 EXPECT_EQ(
606 GetCaretBoundsForPosition(client, test_string, kTextLength, &caret_range),
607 gfx::ScreenRectFromNSRect(rect));
608 EXPECT_EQ_RANGE(query_range, actual_range);
609
610 // Query outside composition range. Ensure bounds and range corresponding to
611 // the current caret position are returned.
612 query_range = NSMakeRange(kTextLength + 1, 0);
613 rect = [ns_view_ firstRectForCharacterRange:query_range
614 actualRange:&actual_range];
615 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
616 EXPECT_EQ_RANGE(caret_range, actual_range);
617
618 // Make sure not crashing by passing null pointer instead of actualRange.
619 rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:nullptr];
620 }
621
622 // Test firstRectForCharacterRange:actualRange for non-empty query ranges within
623 // the composition range.
624 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) {
625 InstallTextField("");
626 ui::TextInputClient* client = [ns_view_ textInputClient];
627
628 const base::string16 kTestString = base::ASCIIToUTF16("test_str");
629 const size_t kTextLength = 8;
630 SetCompositionText(client, kTestString, 1, nullptr);
631
632 // Query bounds for the whole composition string.
633 NSRange query_range = NSMakeRange(0, kTextLength);
634 NSRange actual_range;
635 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
636 actualRange:&actual_range];
637 EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range),
638 gfx::ScreenRectFromNSRect(rect));
639 EXPECT_EQ_RANGE(query_range, actual_range);
640
641 // Query bounds for the substring "est_".
642 query_range = NSMakeRange(1, 4);
643 rect = [ns_view_ firstRectForCharacterRange:query_range
644 actualRange:&actual_range];
645 EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range),
646 gfx::ScreenRectFromNSRect(rect));
647 EXPECT_EQ_RANGE(query_range, actual_range);
648 }
649
508 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; 650 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest;
509 651
510 // Simulate the notifications that AppKit would send out if a fullscreen 652 // Simulate the notifications that AppKit would send out if a fullscreen
511 // operation begins, and then fails and must abort. This notification sequence 653 // operation begins, and then fails and must abort. This notification sequence
512 // was determined by posting delayed tasks to toggle fullscreen state and then 654 // 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 655 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause
514 // the fullscreen transition to fail. 656 // the fullscreen transition to fail.
515 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { 657 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) {
516 if (base::mac::IsOSSnowLeopard()) 658 if (base::mac::IsOSSnowLeopard())
517 return; 659 return;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 [center postNotificationName:NSWindowDidExitFullScreenNotification 718 [center postNotificationName:NSWindowDidExitFullScreenNotification
577 object:window]; 719 object:window];
578 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. 720 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change.
579 EXPECT_FALSE(bridge()->target_fullscreen_state()); 721 EXPECT_FALSE(bridge()->target_fullscreen_state());
580 722
581 widget_->CloseNow(); 723 widget_->CloseNow();
582 } 724 }
583 725
584 } // namespace test 726 } // namespace test
585 } // namespace views 727 } // 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