Chromium Code Reviews| 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 #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" |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 36 EXPECT_EQ(a.location, b.location); \ | 36 EXPECT_EQ(a.location, b.location); \ |
| 37 EXPECT_EQ(a.length, b.length); | 37 EXPECT_EQ(a.length, b.length); |
| 38 | 38 |
| 39 namespace { | 39 namespace { |
| 40 | 40 |
| 41 // Empty range shortcut for readibility. | 41 // Empty range shortcut for readibility. |
| 42 NSRange EmptyRange() { | 42 NSRange EmptyRange() { |
| 43 return NSMakeRange(NSNotFound, 0); | 43 return NSMakeRange(NSNotFound, 0); |
| 44 } | 44 } |
| 45 | 45 |
| 46 // Changes NSRect to gfx::Rect and converts it from Apple's coordinate system | |
| 47 // (origin at bottom left) to coordinate system with origin at top left. | |
| 48 gfx::Rect ConvertCoordinateSystem(NSRect rect) { | |
|
tapted
2015/12/17 08:42:20
gfx::ScreenRectFromNSRect
karandeepb
2015/12/18 09:15:06
Done.
| |
| 49 NSRect screen_rect = [[NSScreen mainScreen] frame]; | |
| 50 rect.origin.y = screen_rect.size.height - NSMaxY(rect); | |
| 51 return gfx::Rect(NSRectToCGRect(rect)); | |
| 52 } | |
| 53 | |
| 46 } // namespace | 54 } // namespace |
| 47 | 55 |
| 48 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates | 56 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates |
| 49 // NSWindow's behavior when attempting to toggle fullscreen state again, when | 57 // NSWindow's behavior when attempting to toggle fullscreen state again, when |
| 50 // the last attempt failed but Cocoa has not yet sent | 58 // the last attempt failed but Cocoa has not yet sent |
| 51 // windowDidFailToEnterFullScreen:. | 59 // windowDidFailToEnterFullScreen:. |
| 52 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow { | 60 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow { |
| 53 @private | 61 @private |
| 54 int ignoredToggleFullScreenCount_; | 62 int ignoredToggleFullScreenCount_; |
| 55 } | 63 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 // before the tests covering the Init() flow are ready to do that. | 129 // before the tests covering the Init() flow are ready to do that. |
| 122 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; | 130 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
| 123 | 131 |
| 124 // To control the lifetime without an actual window that must be closed, | 132 // To control the lifetime without an actual window that must be closed, |
| 125 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. | 133 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. |
| 126 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 134 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 127 | 135 |
| 128 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. | 136 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. |
| 129 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; | 137 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; |
| 130 | 138 |
| 139 init_params_.bounds = gfx::Rect(100, 100, 100, 100); | |
| 140 | |
| 131 native_widget_mac_->GetWidget()->Init(init_params_); | 141 native_widget_mac_->GetWidget()->Init(init_params_); |
| 132 } | 142 } |
| 133 | 143 |
| 134 protected: | 144 protected: |
| 135 scoped_ptr<Widget> widget_; | 145 scoped_ptr<Widget> widget_; |
| 136 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. | 146 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. |
| 137 | 147 |
| 138 // Make the InitParams available to tests to cover initialization codepaths. | 148 // Make the InitParams available to tests to cover initialization codepaths. |
| 139 Widget::InitParams init_params_; | 149 Widget::InitParams init_params_; |
| 140 }; | 150 }; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 167 | 177 |
| 168 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { | 178 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { |
| 169 } | 179 } |
| 170 | 180 |
| 171 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { | 181 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { |
| 172 } | 182 } |
| 173 | 183 |
| 174 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { | 184 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { |
| 175 Textfield* textfield = new Textfield(); | 185 Textfield* textfield = new Textfield(); |
| 176 textfield->SetText(ASCIIToUTF16(text)); | 186 textfield->SetText(ASCIIToUTF16(text)); |
| 187 textfield->SetBoundsRect(init_params_.bounds); | |
| 177 view_->AddChildView(textfield); | 188 view_->AddChildView(textfield); |
| 178 | 189 |
| 179 // Request focus so the InputMethod can dispatch events to the RootView, and | 190 // Request focus so the InputMethod can dispatch events to the RootView, and |
| 180 // have them delivered to the textfield. Note that focusing a textfield | 191 // have them delivered to the textfield. Note that focusing a textfield |
| 181 // schedules a task to flash the cursor, so this requires |message_loop_|. | 192 // schedules a task to flash the cursor, so this requires |message_loop_|. |
| 182 textfield->RequestFocus(); | 193 textfield->RequestFocus(); |
| 183 | 194 |
| 184 [ns_view_ setTextInputClient:textfield]; | 195 [ns_view_ setTextInputClient:textfield]; |
| 185 } | 196 } |
| 186 | 197 |
| (...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 498 EXPECT_EQ("a", GetText()); | 509 EXPECT_EQ("a", GetText()); |
| 499 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); | 510 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); |
| 500 | 511 |
| 501 // Should succeed after moving left first. | 512 // Should succeed after moving left first. |
| 502 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; | 513 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; |
| 503 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; | 514 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; |
| 504 EXPECT_EQ("", GetText()); | 515 EXPECT_EQ("", GetText()); |
| 505 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); | 516 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); |
| 506 } | 517 } |
| 507 | 518 |
| 519 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) { | |
| 520 InstallTextField(""); | |
| 521 ui::TextInputClient* textInputClient = [ns_view_ textInputClient]; | |
|
tapted
2015/12/17 08:42:20
hacker_style (eg. `client` for brevity too)
karandeepb
2015/12/18 09:15:06
Done.
| |
| 522 NSRange range; | |
| 523 NSRange actual_range = NSMakeRange(-1, -1); | |
| 524 NSRect rect; | |
| 525 gfx::Rect bounds; | |
| 526 | |
| 527 // Empty composition. | |
| 528 rect = [ns_view_ firstRectForCharacterRange:NSMakeRange(0, 0) | |
| 529 actualRange:&actual_range]; | |
| 530 bounds = textInputClient->GetCaretBounds(); | |
| 531 bounds.set_width(0); | |
| 532 EXPECT_TRUE(ConvertCoordinateSystem(rect) == bounds); | |
| 533 EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0), actual_range)); | |
| 534 | |
| 535 rect = [ns_view_ firstRectForCharacterRange:NSMakeRange(1, 1) | |
| 536 actualRange:&actual_range]; | |
| 537 EXPECT_TRUE(ConvertCoordinateSystem(rect) == bounds); | |
| 538 EXPECT_TRUE(NSEqualRanges(NSMakeRange(1, 0), actual_range)); | |
| 539 | |
| 540 // Set composition with caret before second character('e'). | |
| 541 ui::CompositionText composition; | |
| 542 composition.selection = gfx::Range(1); | |
| 543 composition.text = base::UTF8ToUTF16("test_str"); | |
| 544 size_t count = composition.text.length(); | |
| 545 textInputClient->SetCompositionText(composition); | |
| 546 | |
| 547 range = NSMakeRange(1, 0); | |
| 548 rect = [ns_view_ firstRectForCharacterRange:range actualRange:&actual_range]; | |
| 549 bounds = textInputClient->GetCaretBounds(); | |
| 550 bounds.set_width(0); | |
| 551 EXPECT_TRUE(ConvertCoordinateSystem(rect) == bounds); | |
| 552 EXPECT_TRUE(NSEqualRanges(range, actual_range)); | |
| 553 | |
| 554 range = NSMakeRange(2, 0); | |
| 555 rect = [ns_view_ firstRectForCharacterRange:range actualRange:&actual_range]; | |
| 556 EXPECT_FALSE(ConvertCoordinateSystem(rect) == bounds); | |
| 557 EXPECT_TRUE(NSEqualRanges(range, actual_range)); | |
| 558 | |
| 559 // Query outside composition range. | |
| 560 range = NSMakeRange(count + 1, 0); | |
| 561 rect = [ns_view_ firstRectForCharacterRange:range actualRange:&actual_range]; | |
| 562 EXPECT_TRUE(ConvertCoordinateSystem(rect) == bounds); | |
| 563 EXPECT_TRUE(NSEqualRanges(range, actual_range)); | |
| 564 | |
| 565 std::vector<gfx::Rect> char_bounds, caret_bounds; | |
|
tapted
2015/12/17 08:42:20
it's uncommon to declare multiple things on a line
karandeepb
2015/12/18 09:15:06
Done.
| |
| 566 char_bounds.resize(count); | |
| 567 caret_bounds.resize(count + 1); | |
| 568 | |
| 569 // Generate caret_bounds between different characters. | |
| 570 for (size_t i = 0; i <= count; i++) { | |
| 571 composition.selection = gfx::Range(i); | |
| 572 textInputClient->SetCompositionText(composition); | |
| 573 caret_bounds[i] = textInputClient->GetCaretBounds(); | |
| 574 } | |
| 575 | |
| 576 // Generate individual character bounds from caret positions. | |
| 577 for (size_t i = 0; i < count; i++) { | |
| 578 char_bounds[i].set_origin(caret_bounds[i].origin()); | |
| 579 char_bounds[i].set_width(caret_bounds[i + 1].x() - caret_bounds[i].x()); | |
| 580 char_bounds[i].set_height( | |
| 581 std::max(caret_bounds[i].height(), caret_bounds[i + 1].height())); | |
| 582 } | |
| 583 | |
| 584 // Verify bounds for all valid ranges. | |
| 585 for (size_t i = 0; i < count; i++) { | |
| 586 for (size_t j = i + 1; j <= count; j++) { | |
| 587 range = NSMakeRange(i, j - i); | |
| 588 rect = | |
| 589 [ns_view_ firstRectForCharacterRange:range actualRange:&actual_range]; | |
| 590 | |
| 591 bounds = gfx::Rect(); | |
| 592 for (size_t k = i; k < j; k++) | |
| 593 bounds.Union(char_bounds[k]); | |
| 594 | |
| 595 EXPECT_TRUE(ConvertCoordinateSystem(rect) == bounds); | |
| 596 EXPECT_TRUE(NSEqualRanges(range, actual_range)); | |
| 597 } | |
| 598 } | |
| 599 | |
| 600 // Check NSZeroRect is returned if bridged_content_view has no textInputClient | |
| 601 // available. | |
| 602 [ns_view_ setTextInputClient:nil]; | |
| 603 rect = [ns_view_ firstRectForCharacterRange:NSMakeRange(0, 0) | |
| 604 actualRange:&actual_range]; | |
| 605 EXPECT_TRUE(NSEqualRects(rect, NSZeroRect)); | |
| 606 } | |
| 607 | |
| 508 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; | 608 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; |
| 509 | 609 |
| 510 // Simulate the notifications that AppKit would send out if a fullscreen | 610 // Simulate the notifications that AppKit would send out if a fullscreen |
| 511 // operation begins, and then fails and must abort. This notification sequence | 611 // operation begins, and then fails and must abort. This notification sequence |
| 512 // was determined by posting delayed tasks to toggle fullscreen state and then | 612 // 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 | 613 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause |
| 514 // the fullscreen transition to fail. | 614 // the fullscreen transition to fail. |
| 515 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { | 615 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { |
| 516 if (base::mac::IsOSSnowLeopard()) | 616 if (base::mac::IsOSSnowLeopard()) |
| 517 return; | 617 return; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 576 [center postNotificationName:NSWindowDidExitFullScreenNotification | 676 [center postNotificationName:NSWindowDidExitFullScreenNotification |
| 577 object:window]; | 677 object:window]; |
| 578 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. | 678 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. |
| 579 EXPECT_FALSE(bridge()->target_fullscreen_state()); | 679 EXPECT_FALSE(bridge()->target_fullscreen_state()); |
| 580 | 680 |
| 581 widget_->CloseNow(); | 681 widget_->CloseNow(); |
| 582 } | 682 } |
| 583 | 683 |
| 584 } // namespace test | 684 } // namespace test |
| 585 } // namespace views | 685 } // namespace views |
| OLD | NEW |