Commit 63b9c45c authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

RemoteRWHVMac: Mirror TextSelection and CompositionRangeInfo in NSView

Whenever the TextInputManager::TextSelection or the
TextInputManager::CompositionRangeInfo properties of the RWHVMac change,
forward them to the RWHVCocoa. This is in contrast to the previous
behavior where we would do a synchronous IPC back to the RWHVMac to
query their value.

This is motivated by the attributedSubstringForProposedRange method
in RWHVMac, which would be complicated to implement by a synchronous
IPC.

Remove RWHVCocoa's selectedRange_ member because it is now fully
redundant to the cached copy of the selected range. Leave RWHVCocoa's
markedRange_ member because it is not always equal to the cached
copy of the composition range.

While converting attributedSubstringForProposedRange, fix an overflow
bug, and remove some roll-your-own NSString<->base::string16
conversions.

Rename ClearMarkedText to CancelComposition.

Bug: 821651
Change-Id: Ieace82d061f15c510762d37e110c70b10eeb2bde
Reviewed-on: https://chromium-review.googlesource.com/1007981
Commit-Queue: ccameron <ccameron@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550545}
parent 40ae08bc
......@@ -66,10 +66,16 @@ class RenderWidgetHostNSViewBridge {
// Call the -[NSView setToolTipAtMousePoint] method.
virtual void SetTooltipText(const base::string16& display_text) = 0;
// Set or clear the marked and selected range.
virtual void SetMarkedRange(const gfx::Range& range) = 0;
virtual void ClearMarkedText() = 0;
virtual void SetSelectedRange(const gfx::Range& range) = 0;
// Forward the TextInputManager::TextSelection from the renderer.
virtual void SetTextSelection(const base::string16& text,
size_t offset,
const gfx::Range& range) = 0;
// Forward the TextInputManager::CompositionRangeInfo from the renderer.
virtual void SetCompositionRangeInfo(const gfx::Range& range) = 0;
// Clear the marked range.
virtual void CancelComposition() = 0;
// Indicate if the WebContext is showing a context menu.
virtual void SetShowingContextMenu(bool showing) = 0;
......
......@@ -42,9 +42,11 @@ class RenderWidgetHostViewNSViewBridgeLocal
void SetBackgroundColor(SkColor color) override;
void SetVisible(bool visible) override;
void SetTooltipText(const base::string16& display_text) override;
void SetMarkedRange(const gfx::Range& range) override;
void ClearMarkedText() override;
void SetSelectedRange(const gfx::Range& range) override;
void SetTextSelection(const base::string16& text,
size_t offset,
const gfx::Range& range) override;
void SetCompositionRangeInfo(const gfx::Range& range) override;
void CancelComposition() override;
void SetShowingContextMenu(bool showing) override;
void DisplayCursor(const WebCursor& cursor) override;
void ShowDictionaryOverlayForSelection() override;
......@@ -202,18 +204,21 @@ void RenderWidgetHostViewNSViewBridgeLocal::SetTooltipText(
[cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
}
void RenderWidgetHostViewNSViewBridgeLocal::SetMarkedRange(
void RenderWidgetHostViewNSViewBridgeLocal::SetCompositionRangeInfo(
const gfx::Range& range) {
[cocoa_view_ setCompositionRange:range];
[cocoa_view_ setMarkedRange:range.ToNSRange()];
}
void RenderWidgetHostViewNSViewBridgeLocal::ClearMarkedText() {
void RenderWidgetHostViewNSViewBridgeLocal::CancelComposition() {
[cocoa_view_ cancelComposition];
}
void RenderWidgetHostViewNSViewBridgeLocal::SetSelectedRange(
void RenderWidgetHostViewNSViewBridgeLocal::SetTextSelection(
const base::string16& text,
size_t offset,
const gfx::Range& range) {
[cocoa_view_ setSelectedRange:range.ToNSRange()];
[cocoa_view_ setTextSelectionText:text offset:offset range:range];
// Updates markedRange when there is no marked text so that retrieving
// markedRange immediately after calling setMarkdText: returns the current
// caret position.
......
......@@ -124,12 +124,6 @@ class RenderWidgetHostNSViewClient {
virtual void OnNSViewSyncGetTextInputType(
ui::TextInputType* text_input_type) = 0;
// Synchronously query the current selection. If there exists a selection then
// set |*has_selection| to true and return in |*selected_text| the selected
// text. Otherwise set |*has_selection| to false.
virtual void OnNSViewSyncGetSelectedText(bool* has_selection,
base::string16* selected_text) = 0;
// Synchronously query the character index for |root_point| and return it in
// |*index|. Sets it to UINT32_MAX if the request fails or is not completed.
virtual void OnNSViewSyncGetCharacterIndexAtPoint(
......
......@@ -115,8 +115,17 @@ struct DidOverscrollParams;
// the whole content yet.
NSRange markedRange_;
// The selected range, cached from a message sent by the renderer.
NSRange selectedRange_;
// The text selection, cached from the RenderWidgetHostView. This is only ever
// updated from the renderer.
base::string16 textSelectionText_;
size_t textSelectionOffset_;
gfx::Range textSelectionRange_;
// The composition range, cached from the RenderWidgetHostView. This is only
// ever updated from the renderer (unlike |markedRange_|, which sometimes but
// not always coincides with |compositionRange_|).
bool hasCompositionRange_;
gfx::Range compositionRange_;
// Text to be inserted which was generated by handling a key down event.
base::string16 textToBeInserted_;
......@@ -163,7 +172,6 @@ struct DidOverscrollParams;
content::MouseWheelRailsFilterMac mouseWheelFilter_;
}
@property(nonatomic, assign) NSRange selectedRange;
@property(nonatomic, assign) NSRange markedRange;
// Common code path for handling begin gesture events. This helper method is
......@@ -204,7 +212,13 @@ struct DidOverscrollParams;
- (void)updateScreenProperties;
// Indicate if the embedding WebContents is showing a web content context menu.
- (void)setShowingContextMenu:(BOOL)showing;
// Set the current TextInputManager::TextSelection from the renderer.
- (void)setTextSelectionText:(base::string16)text
offset:(size_t)offset
range:(gfx::Range)range;
- (base::string16)selectedText;
// Set the current TextInputManager::CompositionRangeInfo from the renderer.
- (void)setCompositionRange:(gfx::Range)range;
// Methods previously marked as private.
- (id)initWithClient:
(std::unique_ptr<content::RenderWidgetHostNSViewClient>)client;
......
......@@ -123,7 +123,6 @@ void ExtractUnderlines(NSAttributedString* string,
@end
@implementation RenderWidgetHostViewCocoa
@synthesize selectedRange = selectedRange_;
@synthesize markedRange = markedRange_;
- (id)initWithClient:(std::unique_ptr<RenderWidgetHostNSViewClient>)client {
......@@ -188,6 +187,29 @@ void ExtractUnderlines(NSAttributedString* string,
client_->OnNSViewBoundsInWindowChanged(gfxViewBoundsInWindow, true);
}
- (void)setTextSelectionText:(base::string16)text
offset:(size_t)offset
range:(gfx::Range)range {
textSelectionText_ = text;
textSelectionOffset_ = offset;
textSelectionRange_ = range;
}
- (base::string16)selectedText {
gfx::Range textRange(textSelectionOffset_,
textSelectionOffset_ + textSelectionText_.size());
gfx::Range intersectionRange = textRange.Intersect(textSelectionRange_);
if (intersectionRange.is_empty())
return base::string16();
return textSelectionText_.substr(
intersectionRange.start() - textSelectionOffset_,
intersectionRange.length());
}
- (void)setCompositionRange:(gfx::Range)range {
compositionRange_ = range;
}
- (void)sendWindowFrameInScreenToClient {
TRACE_EVENT0("browser",
"RenderWidgetHostViewCocoa::sendWindowFrameInScreenToClient");
......@@ -1418,9 +1440,7 @@ extern NSString* NSTextInputReplacementRangeAttributeName;
}
- (NSRange)selectedRange {
if (selectedRange_.location == NSNotFound)
return NSMakeRange(NSNotFound, 0);
return selectedRange_;
return textSelectionRange_.ToNSRange();
}
- (NSRange)markedRange {
......@@ -1448,52 +1468,37 @@ extern NSString* NSTextInputReplacementRangeAttributeName;
if (range.length >= std::numeric_limits<NSUInteger>::max() - range.location)
return nil;
const gfx::Range requested_range(range);
if (requested_range.is_reversed())
const gfx::Range requestedRange(range);
if (requestedRange.is_reversed())
return nil;
gfx::Range expected_range;
const base::string16* expected_text;
const content::TextInputManager::CompositionRangeInfo* compositionInfo =
renderWidgetHostView_->GetCompositionRangeInfo();
const content::TextInputManager::TextSelection* selection =
renderWidgetHostView_->GetTextSelection();
if (!selection)
return nil;
gfx::Range expectedRange;
const base::string16* expectedText;
if (compositionInfo && !compositionInfo->range.is_empty()) {
if (!compositionRange_.is_empty()) {
// This method might get called after TextInputState.type is reset to none,
// in which case there will be no composition range information
// (https://crbug.com/698672).
expected_text = &markedText_;
expected_range = compositionInfo->range;
// https://crbug.com/698672
expectedText = &markedText_;
expectedRange = compositionRange_.Intersect(
gfx::Range(compositionRange_.start(),
compositionRange_.start() + expectedText->length()));
} else {
expected_text = &selection->text();
size_t offset = selection->offset();
expected_range = gfx::Range(offset, offset + expected_text->size());
expectedText = &textSelectionText_;
size_t offset = textSelectionOffset_;
expectedRange = gfx::Range(offset, offset + expectedText->size());
}
gfx::Range actual_range = expected_range.Intersect(requested_range);
if (!actual_range.IsValid())
gfx::Range gfxActualRange = expectedRange.Intersect(requestedRange);
if (!gfxActualRange.IsValid())
return nil;
// Gets the raw bytes to avoid unnecessary string copies for generating
// NSString.
const base::char16* bytes =
&(*expected_text)[actual_range.start() - expected_range.start()];
// Avoid integer overflow.
base::CheckedNumeric<size_t> requested_len = actual_range.length();
requested_len *= sizeof(base::char16);
NSUInteger bytes_len =
base::strict_cast<NSUInteger, size_t>(requested_len.ValueOrDefault(0));
base::scoped_nsobject<NSString> ns_string([[NSString alloc]
initWithBytes:bytes
length:bytes_len
encoding:NSUTF16LittleEndianStringEncoding]);
if (actualRange)
*actualRange = actual_range.ToNSRange();
*actualRange = gfxActualRange.ToNSRange();
return [[[NSAttributedString alloc] initWithString:ns_string] autorelease];
base::string16 string = expectedText->substr(
gfxActualRange.start() - expectedRange.start(), gfxActualRange.length());
return [[[NSAttributedString alloc]
initWithString:base::SysUTF16ToNSString(string)] autorelease];
}
- (NSInteger)conversationIdentifier {
......@@ -1749,14 +1754,11 @@ extern NSString* NSTextInputReplacementRangeAttributeName;
returnType:(NSString*)returnType {
ui::TextInputType textInputType = ui::TEXT_INPUT_TYPE_NONE;
client_->OnNSViewSyncGetTextInputType(&textInputType);
bool hasSelection = false;
base::string16 selectedText;
client_->OnNSViewSyncGetSelectedText(&hasSelection, &selectedText);
id requestor = nil;
BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
BOOL hasText = hasSelection && !selectedText.empty();
BOOL hasText = !textSelectionRange_.is_empty();
BOOL takesText = textInputType != ui::TEXT_INPUT_TYPE_NONE;
if (sendTypeIsString && hasText && !returnType) {
......@@ -1799,16 +1801,12 @@ extern NSString* NSTextInputReplacementRangeAttributeName;
@implementation RenderWidgetHostViewCocoa (NSServicesRequests)
- (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
bool hasSelection = false;
base::string16 selectedText;
client_->OnNSViewSyncGetSelectedText(&hasSelection, &selectedText);
if (!hasSelection || selectedText.empty() ||
if (textSelectionRange_.is_empty() ||
![types containsObject:NSStringPboardType]) {
return NO;
}
base::scoped_nsobject<NSString> text([[NSString alloc]
initWithUTF8String:base::UTF16ToUTF8(selectedText).c_str()]);
NSString* text = base::SysUTF16ToNSString([self selectedText]);
NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
[pboard declareTypes:toDeclare owner:nil];
return [pboard setString:text forType:NSStringPboardType];
......
......@@ -327,8 +327,6 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
const gfx::Range& range) override;
void OnNSViewSyncGetTextInputType(
ui::TextInputType* text_input_type) override;
void OnNSViewSyncGetSelectedText(bool* has_selection,
base::string16* selected_text) override;
void OnNSViewSyncGetCharacterIndexAtPoint(const gfx::PointF& root_point,
uint32_t* index) override;
void OnNSViewSyncGetFirstRectForRange(const gfx::Range& requested_range,
......
......@@ -510,7 +510,7 @@ void RenderWidgetHostViewMac::OnUpdateTextInputStateCalled(
void RenderWidgetHostViewMac::OnImeCancelComposition(
TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view) {
ns_view_bridge_->ClearMarkedText();
ns_view_bridge_->CancelComposition();
}
void RenderWidgetHostViewMac::OnImeCompositionRangeChanged(
......@@ -522,7 +522,7 @@ void RenderWidgetHostViewMac::OnImeCompositionRangeChanged(
return;
// The RangeChanged message is only sent with valid values. The current
// caret position (start == end) will be sent if there is no IME range.
ns_view_bridge_->SetMarkedRange(info->range);
ns_view_bridge_->SetCompositionRangeInfo(info->range);
}
void RenderWidgetHostViewMac::OnSelectionBoundsChanged(
......@@ -566,8 +566,8 @@ void RenderWidgetHostViewMac::OnTextSelectionChanged(
const TextInputManager::TextSelection* selection = GetTextSelection();
if (!selection)
return;
ns_view_bridge_->SetSelectedRange(selection->range());
ns_view_bridge_->SetTextSelection(selection->text(), selection->offset(),
selection->range());
}
void RenderWidgetHostViewMac::OnRenderFrameMetadataChanged() {
......@@ -923,7 +923,7 @@ bool RenderWidgetHostViewMac::ShouldContinueToPauseForFrame() {
void RenderWidgetHostViewMac::FocusedNodeChanged(
bool is_editable_node,
const gfx::Rect& node_bounds_in_screen) {
ns_view_bridge_->ClearMarkedText();
ns_view_bridge_->CancelComposition();
// If the Mac Zoom feature is enabled, update it with the bounds of the
// current focused node so that it can ensure that it's scrolled into view.
......@@ -1557,18 +1557,6 @@ void RenderWidgetHostViewMac::OnNSViewSyncGetTextInputType(
*text_input_type = GetTextInputType();
}
void RenderWidgetHostViewMac::OnNSViewSyncGetSelectedText(
bool* has_selection,
base::string16* selected_text) {
const TextInputManager::TextSelection* selection = GetTextSelection();
if (selection) {
*has_selection = true;
*selected_text = selection->selected_text();
} else {
*has_selection = false;
}
}
void RenderWidgetHostViewMac::OnNSViewSyncGetCharacterIndexAtPoint(
const gfx::PointF& root_point,
uint32_t* index) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment