Index: content/browser/renderer_host/render_widget_host_view_mac.mm |
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm |
index e6bfb673c4ff0c1c50a7a35d4435727b3d4ced9f..de19726cdac80863418d359d264089cc09998794 100644 |
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm |
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm |
@@ -635,6 +635,8 @@ void RenderWidgetHostViewMac::TextInputStateChanged( |
void RenderWidgetHostViewMac::SelectionBoundsChanged( |
const gfx::Rect& start_rect, |
const gfx::Rect& end_rect) { |
+ if (start_rect == end_rect) |
+ caret_rect_ = start_rect; |
} |
void RenderWidgetHostViewMac::ImeCancelComposition() { |
@@ -647,6 +649,8 @@ void RenderWidgetHostViewMac::ImeCompositionRangeChanged( |
// The RangeChanged message is only sent with valid values. The current |
// caret position (start == end) will be sent if there is no IME range. |
[cocoa_view_ setMarkedRange:range.ToNSRange()]; |
+ composition_range_ = range; |
+ composition_bounds_ = character_bounds; |
} |
void RenderWidgetHostViewMac::DidUpdateBackingStore( |
@@ -790,6 +794,8 @@ void RenderWidgetHostViewMac::SelectionChanged(const string16& text, |
if (![cocoa_view_ hasMarkedText]) { |
[cocoa_view_ setMarkedRange:range.ToNSRange()]; |
} |
+ |
+ RenderWidgetHostViewBase::SelectionChanged(text, offset, range); |
} |
void RenderWidgetHostViewMac::SetShowingContextMenu(bool showing) { |
@@ -1039,6 +1045,122 @@ void RenderWidgetHostViewMac::AckPendingSwapBuffers() { |
} |
} |
+bool RenderWidgetHostViewMac::GetLineBreakIdx( |
+ const std::vector<gfx::Rect>& bounds, |
+ const ui::Range& range, |
+ size_t* line_break_point) { |
+ DCHECK(line_break_point); |
+ if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty()) |
+ return false; |
+ |
+ // We can't check line breaking completely from only rectangle array. Thus we |
+ // assume the line breaking as the next character's y offset is larger than |
+ // a threshold. Currently the threshold is determined as minimum y offset plus |
+ // 75% of maximum height. |
+ // TODO(nona): Check the threshold is reliable or not. |
+ // TODO(nona): Bidi support. |
+ const size_t loop_end_idx = std::min(bounds.size(), range.end()); |
+ int max_height = 0; |
+ int min_y_offset = kint32max; |
+ for (size_t idx = range.start(); idx < loop_end_idx; ++idx) { |
+ max_height = std::max(max_height, bounds[idx].height()); |
+ min_y_offset = std::min(min_y_offset, bounds[idx].y()); |
+ } |
+ int line_break_threshold = min_y_offset + (max_height * 3 / 4); |
+ for (size_t idx = range.start(); idx < loop_end_idx; ++idx) { |
+ if (bounds[idx].y() > line_break_threshold) { |
+ *line_break_point = idx; |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange( |
+ const ui::Range& range, |
+ ui::Range* actual_range) { |
+ DCHECK(actual_range); |
+ DCHECK(!composition_bounds_.empty()); |
+ DCHECK(range.start() <= composition_bounds_.size()); |
+ DCHECK(range.end() <= composition_bounds_.size()); |
+ |
+ if (range.is_empty()) { |
+ *actual_range = range; |
+ if (range.start() == composition_bounds_.size()) { |
+ return gfx::Rect(composition_bounds_[range.start() - 1].right(), |
+ composition_bounds_[range.start() - 1].y(), |
+ 0, |
+ composition_bounds_[range.start() - 1].height()); |
+ } else { |
+ return gfx::Rect(composition_bounds_[range.start()].x(), |
+ composition_bounds_[range.start()].y(), |
+ 0, |
+ composition_bounds_[range.start()].height()); |
+ } |
+ } |
+ |
+ size_t end_idx; |
+ if (!GetLineBreakIdx(composition_bounds_, range, &end_idx)) { |
+ end_idx = range.end(); |
+ } |
+ *actual_range = ui::Range(range.start(), end_idx); |
+ gfx::Rect rect = composition_bounds_[range.start()]; |
+ for (size_t i = range.start() + 1; i < end_idx; ++i) { |
+ rect = rect.Union(composition_bounds_[i]); |
+ } |
+ return rect; |
+} |
+ |
+ui::Range RenderWidgetHostViewMac::ConvertCharacterRangeToCompositionRange( |
+ const ui::Range& request_range) { |
+ if (composition_range_.is_empty()) |
+ return ui::Range::InvalidRange(); |
+ |
+ if (request_range.is_reversed()) |
+ return ui::Range::InvalidRange(); |
+ |
+ if (request_range.start() < composition_range_.start() || |
+ request_range.start() > composition_range_.end() || |
+ request_range.end() > composition_range_.end()) { |
+ return ui::Range::InvalidRange(); |
+ } |
+ |
+ return ui::Range( |
+ request_range.start() - composition_range_.start(), |
+ request_range.end() - composition_range_.start()); |
+} |
+ |
+bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange( |
+ NSRange range, |
Nico
2012/07/21 16:43:27
nit: indent arguments 4
Seigo Nonaka
2012/07/23 08:36:55
Done.
|
+ NSRect* rect, |
+ NSRange* actual_range) { |
Nico
2012/07/21 16:43:27
Add a comment "// This exists to make IMEs more re
Seigo Nonaka
2012/07/23 08:36:55
Done.
|
+ TRACE_EVENT0("browser", |
+ "RenderWidgetHostViewMac::GetFirstRectForCharacterRange"); |
+ |
+ // If requested range is same as caret location, we can just return it. |
+ if (selection_range_.is_empty() && ui::Range(range) == selection_range_) { |
+ *actual_range = range; |
+ *rect = NSRectFromCGRect(caret_rect_.ToCGRect()); |
+ return true; |
+ } |
+ |
+ const ui::Range request_range_in_composition = |
+ ConvertCharacterRangeToCompositionRange(ui::Range(range)); |
Nico
2012/07/21 16:43:27
nit: indent continuations 2 more
Seigo Nonaka
2012/07/23 08:36:55
Done.
|
+ if (request_range_in_composition == ui::Range::InvalidRange()) |
+ return false; |
+ |
+ ui::Range ui_actual_range; |
+ *rect = NSRectFromCGRect(GetFirstRectForCompositionRange( |
+ request_range_in_composition, |
+ &ui_actual_range).ToCGRect()); |
+ if (actual_range) { |
+ *actual_range = ui::Range( |
+ composition_range_.start() + ui_actual_range.start(), |
+ composition_range_.start() + ui_actual_range.end()).ToNSRange(); |
+ } |
+ return true; |
+} |
+ |
void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( |
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, |
int gpu_host_id) { |
@@ -2681,11 +2803,18 @@ extern NSString *NSTextInputReplacementRangeAttributeName; |
- (NSRect)firstRectForCharacterRange:(NSRange)theRange |
actualRange:(NSRangePointer)actualRange { |
- // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery. |
- if (actualRange) |
- *actualRange = theRange; |
- NSRect rect = TextInputClientMac::GetInstance()->GetFirstRectForRange( |
- renderWidgetHostView_->render_widget_host_, theRange); |
+ NSRect rect; |
+ if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange( |
+ theRange, |
+ &rect, |
+ actualRange)) { |
+ rect = TextInputClientMac::GetInstance()->GetFirstRectForRange( |
+ renderWidgetHostView_->render_widget_host_, theRange); |
+ |
+ // TODO(thakis): Pipe |actualRange| through TextInputClientMac machinery. |
+ if (actualRange) |
+ *actualRange = theRange; |
+ } |
// The returned rectangle is in WebKit coordinates (upper left origin), so |
// flip the coordinate system and then convert it into screen coordinates for |