OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "content/browser/renderer_host/render_widget_host_view_mac.h" | 5 #include "content/browser/renderer_host/render_widget_host_view_mac.h" |
6 | 6 |
7 #include <QuartzCore/QuartzCore.h> | 7 #include <QuartzCore/QuartzCore.h> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
608 // See http://crbug.com/73039. | 608 // See http://crbug.com/73039. |
609 [NSApp updateWindows]; | 609 [NSApp updateWindows]; |
610 UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_); | 610 UseInputWindow(TSMGetActiveDocument(), !can_compose_inline_); |
611 } | 611 } |
612 } | 612 } |
613 } | 613 } |
614 | 614 |
615 void RenderWidgetHostViewMac::SelectionBoundsChanged( | 615 void RenderWidgetHostViewMac::SelectionBoundsChanged( |
616 const gfx::Rect& start_rect, | 616 const gfx::Rect& start_rect, |
617 const gfx::Rect& end_rect) { | 617 const gfx::Rect& end_rect) { |
| 618 if (start_rect == end_rect) |
| 619 caret_rect_ = start_rect; |
618 } | 620 } |
619 | 621 |
620 void RenderWidgetHostViewMac::ImeCancelComposition() { | 622 void RenderWidgetHostViewMac::ImeCancelComposition() { |
621 [cocoa_view_ cancelComposition]; | 623 [cocoa_view_ cancelComposition]; |
622 } | 624 } |
623 | 625 |
624 void RenderWidgetHostViewMac::ImeCompositionRangeChanged( | 626 void RenderWidgetHostViewMac::ImeCompositionRangeChanged( |
625 const ui::Range& range, | 627 const ui::Range& range, |
626 const std::vector<gfx::Rect>& character_bounds) { | 628 const std::vector<gfx::Rect>& character_bounds) { |
627 // The RangeChanged message is only sent with valid values. The current | 629 // The RangeChanged message is only sent with valid values. The current |
628 // caret position (start == end) will be sent if there is no IME range. | 630 // caret position (start == end) will be sent if there is no IME range. |
629 [cocoa_view_ setMarkedRange:range.ToNSRange()]; | 631 [cocoa_view_ setMarkedRange:range.ToNSRange()]; |
| 632 composition_range_ = range; |
| 633 composition_bounds_ = character_bounds; |
630 } | 634 } |
631 | 635 |
632 void RenderWidgetHostViewMac::DidUpdateBackingStore( | 636 void RenderWidgetHostViewMac::DidUpdateBackingStore( |
633 const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, | 637 const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, |
634 const std::vector<gfx::Rect>& copy_rects) { | 638 const std::vector<gfx::Rect>& copy_rects) { |
635 GotSoftwareFrame(); | 639 GotSoftwareFrame(); |
636 | 640 |
637 if (!is_hidden_) { | 641 if (!is_hidden_) { |
638 std::vector<gfx::Rect> rects(copy_rects); | 642 std::vector<gfx::Rect> rects(copy_rects); |
639 | 643 |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
763 selected_text_ = UTF16ToUTF8(text.substr(pos, n)); | 767 selected_text_ = UTF16ToUTF8(text.substr(pos, n)); |
764 } | 768 } |
765 | 769 |
766 [cocoa_view_ setSelectedRange:range.ToNSRange()]; | 770 [cocoa_view_ setSelectedRange:range.ToNSRange()]; |
767 // Updaes markedRange when there is no marked text so that retrieving | 771 // Updaes markedRange when there is no marked text so that retrieving |
768 // markedRange immediately after calling setMarkdText: returns the current | 772 // markedRange immediately after calling setMarkdText: returns the current |
769 // caret position. | 773 // caret position. |
770 if (![cocoa_view_ hasMarkedText]) { | 774 if (![cocoa_view_ hasMarkedText]) { |
771 [cocoa_view_ setMarkedRange:range.ToNSRange()]; | 775 [cocoa_view_ setMarkedRange:range.ToNSRange()]; |
772 } | 776 } |
| 777 |
| 778 RenderWidgetHostViewBase::SelectionChanged(text, offset, range); |
773 } | 779 } |
774 | 780 |
775 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) { | 781 void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) { |
776 RenderWidgetHostViewBase::SetShowingContextMenu(showing); | 782 RenderWidgetHostViewBase::SetShowingContextMenu(showing); |
777 | 783 |
778 // Create a fake mouse event to inform the render widget that the mouse | 784 // Create a fake mouse event to inform the render widget that the mouse |
779 // left or entered. | 785 // left or entered. |
780 NSWindow* window = [cocoa_view_ window]; | 786 NSWindow* window = [cocoa_view_ window]; |
781 // TODO(asvitkine): If the location outside of the event stream doesn't | 787 // TODO(asvitkine): If the location outside of the event stream doesn't |
782 // correspond to the current event (due to delayed event processing), then | 788 // correspond to the current event (due to delayed event processing), then |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1012 pending_swap_buffers_acks_.front().first, | 1018 pending_swap_buffers_acks_.front().first, |
1013 pending_swap_buffers_acks_.front().second, | 1019 pending_swap_buffers_acks_.front().second, |
1014 0); | 1020 0); |
1015 if (render_widget_host_) | 1021 if (render_widget_host_) |
1016 render_widget_host_->AcknowledgeSwapBuffersToRenderer(); | 1022 render_widget_host_->AcknowledgeSwapBuffersToRenderer(); |
1017 } | 1023 } |
1018 pending_swap_buffers_acks_.erase(pending_swap_buffers_acks_.begin()); | 1024 pending_swap_buffers_acks_.erase(pending_swap_buffers_acks_.begin()); |
1019 } | 1025 } |
1020 } | 1026 } |
1021 | 1027 |
| 1028 bool RenderWidgetHostViewMac::GetLineBreakIndex( |
| 1029 const std::vector<gfx::Rect>& bounds, |
| 1030 const ui::Range& range, |
| 1031 size_t* line_break_point) { |
| 1032 DCHECK(line_break_point); |
| 1033 if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty()) |
| 1034 return false; |
| 1035 |
| 1036 // We can't check line breaking completely from only rectangle array. Thus we |
| 1037 // assume the line breaking as the next character's y offset is larger than |
| 1038 // a threshold. Currently the threshold is determined as minimum y offset plus |
| 1039 // 75% of maximum height. |
| 1040 // TODO(nona): Check the threshold is reliable or not. |
| 1041 // TODO(nona): Bidi support. |
| 1042 const size_t loop_end_idx = std::min(bounds.size(), range.end()); |
| 1043 int max_height = 0; |
| 1044 int min_y_offset = kint32max; |
| 1045 for (size_t idx = range.start(); idx < loop_end_idx; ++idx) { |
| 1046 max_height = std::max(max_height, bounds[idx].height()); |
| 1047 min_y_offset = std::min(min_y_offset, bounds[idx].y()); |
| 1048 } |
| 1049 int line_break_threshold = min_y_offset + (max_height * 3 / 4); |
| 1050 for (size_t idx = range.start(); idx < loop_end_idx; ++idx) { |
| 1051 if (bounds[idx].y() > line_break_threshold) { |
| 1052 *line_break_point = idx; |
| 1053 return true; |
| 1054 } |
| 1055 } |
| 1056 return false; |
| 1057 } |
| 1058 |
| 1059 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( |
| 1060 const ui::Range& range, |
| 1061 ui::Range* actual_range) { |
| 1062 DCHECK(actual_range); |
| 1063 DCHECK(!composition_bounds_.empty()); |
| 1064 DCHECK(range.start() <= composition_bounds_.size()); |
| 1065 DCHECK(range.end() <= composition_bounds_.size()); |
| 1066 |
| 1067 if (range.is_empty()) { |
| 1068 *actual_range = range; |
| 1069 if (range.start() == composition_bounds_.size()) { |
| 1070 return gfx::Rect(composition_bounds_[range.start() - 1].right(), |
| 1071 composition_bounds_[range.start() - 1].y(), |
| 1072 0, |
| 1073 composition_bounds_[range.start() - 1].height()); |
| 1074 } else { |
| 1075 return gfx::Rect(composition_bounds_[range.start()].x(), |
| 1076 composition_bounds_[range.start()].y(), |
| 1077 0, |
| 1078 composition_bounds_[range.start()].height()); |
| 1079 } |
| 1080 } |
| 1081 |
| 1082 size_t end_idx; |
| 1083 if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) { |
| 1084 end_idx = range.end(); |
| 1085 } |
| 1086 *actual_range = ui::Range(range.start(), end_idx); |
| 1087 gfx::Rect rect = composition_bounds_[range.start()]; |
| 1088 for (size_t i = range.start() + 1; i < end_idx; ++i) { |
| 1089 rect = rect.Union(composition_bounds_[i]); |
| 1090 } |
| 1091 return rect; |
| 1092 } |
| 1093 |
| 1094 ui::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange( |
| 1095 const ui::Range& request_range) { |
| 1096 if (composition_range_.is_empty()) |
| 1097 return ui::Range::InvalidRange(); |
| 1098 |
| 1099 if (request_range.is_reversed()) |
| 1100 return ui::Range::InvalidRange(); |
| 1101 |
| 1102 if (request_range.start() < composition_range_.start() || |
| 1103 request_range.start() > composition_range_.end() || |
| 1104 request_range.end() > composition_range_.end()) { |
| 1105 return ui::Range::InvalidRange(); |
| 1106 } |
| 1107 |
| 1108 return ui::Range( |
| 1109 request_range.start() - composition_range_.start(), |
| 1110 request_range.end() - composition_range_.start()); |
| 1111 } |
| 1112 |
| 1113 bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange( |
| 1114 NSRange range, |
| 1115 NSRect* rect, |
| 1116 NSRange* actual_range) { |
| 1117 // This exists to make IMEs more responsive, see http://crbug.com/115920 |
| 1118 TRACE_EVENT0("browser", |
| 1119 "RenderWidgetHostViewMac::GetFirstRectForCharacterRange"); |
| 1120 |
| 1121 // If requested range is same as caret location, we can just return it. |
| 1122 if (selection_range_.is_empty() && ui::Range(range) == selection_range_) { |
| 1123 *actual_range = range; |
| 1124 *rect = NSRectFromCGRect(caret_rect_.ToCGRect()); |
| 1125 return true; |
| 1126 } |
| 1127 |
| 1128 const ui::Range request_range_in_composition = |
| 1129 ConvertCharacterRangeToCompositionRange(ui::Range(range)); |
| 1130 if (request_range_in_composition == ui::Range::InvalidRange()) |
| 1131 return false; |
| 1132 |
| 1133 ui::Range ui_actual_range; |
| 1134 *rect = NSRectFromCGRect(GetFirstRectForCompositionRange( |
| 1135 request_range_in_composition, |
| 1136 &ui_actual_range).ToCGRect()); |
| 1137 if (actual_range) { |
| 1138 *actual_range = ui::Range( |
| 1139 composition_range_.start() + ui_actual_range.start(), |
| 1140 composition_range_.start() + ui_actual_range.end()).ToNSRange(); |
| 1141 } |
| 1142 return true; |
| 1143 } |
| 1144 |
1022 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( | 1145 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( |
1023 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, | 1146 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, |
1024 int gpu_host_id) { | 1147 int gpu_host_id) { |
1025 TRACE_EVENT0("browser", | 1148 TRACE_EVENT0("browser", |
1026 "RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped"); | 1149 "RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped"); |
1027 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
1028 | 1151 |
1029 pending_swap_buffers_acks_.push_back(std::make_pair(params.route_id, | 1152 pending_swap_buffers_acks_.push_back(std::make_pair(params.route_id, |
1030 gpu_host_id)); | 1153 gpu_host_id)); |
1031 | 1154 |
(...skipping 1623 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2655 | 2778 |
2656 NSUInteger index = | 2779 NSUInteger index = |
2657 TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint( | 2780 TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint( |
2658 renderWidgetHostView_->render_widget_host_, | 2781 renderWidgetHostView_->render_widget_host_, |
2659 gfx::Point(thePoint.x, thePoint.y)); | 2782 gfx::Point(thePoint.x, thePoint.y)); |
2660 return index; | 2783 return index; |
2661 } | 2784 } |
2662 | 2785 |
2663 - (NSRect)firstRectForCharacterRange:(NSRange)theRange | 2786 - (NSRect)firstRectForCharacterRange:(NSRange)theRange |
2664 actualRange:(NSRangePointer)actualRange { | 2787 actualRange:(NSRangePointer)actualRange { |
2665 // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery. | 2788 NSRect rect; |
2666 if (actualRange) | 2789 if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange( |
2667 *actualRange = theRange; | 2790 theRange, |
2668 NSRect rect = TextInputClientMac::GetInstance()->GetFirstRectForRange( | 2791 &rect, |
2669 renderWidgetHostView_->render_widget_host_, theRange); | 2792 actualRange)) { |
| 2793 rect = TextInputClientMac::GetInstance()->GetFirstRectForRange( |
| 2794 renderWidgetHostView_->render_widget_host_, theRange); |
| 2795 |
| 2796 // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery. |
| 2797 if (actualRange) |
| 2798 *actualRange = theRange; |
| 2799 } |
2670 | 2800 |
2671 // The returned rectangle is in WebKit coordinates (upper left origin), so | 2801 // The returned rectangle is in WebKit coordinates (upper left origin), so |
2672 // flip the coordinate system and then convert it into screen coordinates for | 2802 // flip the coordinate system and then convert it into screen coordinates for |
2673 // return. | 2803 // return. |
2674 NSRect viewFrame = [self frame]; | 2804 NSRect viewFrame = [self frame]; |
2675 rect.origin.y = NSHeight(viewFrame) - rect.origin.y; | 2805 rect.origin.y = NSHeight(viewFrame) - rect.origin.y; |
2676 rect.origin.y -= rect.size.height; | 2806 rect.origin.y -= rect.size.height; |
2677 rect = [self convertRectToBase:rect]; | 2807 rect = [self convertRectToBase:rect]; |
2678 rect.origin = [[self window] convertBaseToScreen:rect.origin]; | 2808 rect.origin = [[self window] convertBaseToScreen:rect.origin]; |
2679 return rect; | 2809 return rect; |
(...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3072 if (!string) return NO; | 3202 if (!string) return NO; |
3073 | 3203 |
3074 // If the user is currently using an IME, confirm the IME input, | 3204 // If the user is currently using an IME, confirm the IME input, |
3075 // and then insert the text from the service, the same as TextEdit and Safari. | 3205 // and then insert the text from the service, the same as TextEdit and Safari. |
3076 [self confirmComposition]; | 3206 [self confirmComposition]; |
3077 [self insertText:string]; | 3207 [self insertText:string]; |
3078 return YES; | 3208 return YES; |
3079 } | 3209 } |
3080 | 3210 |
3081 @end | 3211 @end |
OLD | NEW |