summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-02 15:46:01 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-02 15:46:01 +0000
commitd4cff27a73a9dc6caa6b37a8b28ef72ecc35fa0d (patch)
tree22759b5ed8332e11983609a93432dd47e9a65a79
parentc5b003c0bbadd824f84421390945f685f0fc9cd1 (diff)
downloadchromium_src-d4cff27a73a9dc6caa6b37a8b28ef72ecc35fa0d.zip
chromium_src-d4cff27a73a9dc6caa6b37a8b28ef72ecc35fa0d.tar.gz
chromium_src-d4cff27a73a9dc6caa6b37a8b28ef72ecc35fa0d.tar.bz2
[Mac] Implement the system dictionary popup by implementing NSTextInput methods.
This is a two-sided patch; the Chromium side is plumbing to marshall data from the renderer to the system APIs. Note that just hitting Cmd+Ctrl+D usually does not bring up the popup. I think this may be an Apple bug, but I have not yet found a work-around. BUG=17951,37715,47141 TEST=Hold Cmd+Ctrl+D on a web page and mouse around. The dictionary popup should follow the mouse and show the definition of the current word. TEST=In a text area, the dictionary popup should work only if the text area has focus. R=avi,suzhe,jam Review URL: http://codereview.chromium.org/6289009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83723 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chrome_content_browser_client.cc4
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_gtk.cc3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_gtk.h3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.h18
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.mm89
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_views.cc3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_views.h3
-rw-r--r--chrome/browser/renderer_host/text_input_client_mac.h86
-rw-r--r--chrome/browser/renderer_host/text_input_client_mac.mm122
-rw-r--r--chrome/browser/renderer_host/text_input_client_mac_unittest.mm160
-rw-r--r--chrome/browser/renderer_host/text_input_client_message_filter.h44
-rw-r--r--chrome/browser/renderer_host/text_input_client_message_filter.mm63
-rw-r--r--chrome/chrome_browser.gypi4
-rw-r--r--chrome/chrome_common.gypi4
-rw-r--r--chrome/chrome_renderer.gypi2
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/common/attributed_string_coder_mac.h118
-rw-r--r--chrome/common/attributed_string_coder_mac.mm171
-rw-r--r--chrome/common/attributed_string_coder_mac_unittest.mm135
-rw-r--r--chrome/common/text_input_client_messages.cc8
-rw-r--r--chrome/common/text_input_client_messages.h55
-rw-r--r--chrome/renderer/chrome_content_renderer_client.cc5
-rw-r--r--chrome/renderer/text_input_client_observer.cc70
-rw-r--r--chrome/renderer/text_input_client_observer.h41
-rw-r--r--content/browser/renderer_host/render_message_filter.cc2
-rw-r--r--content/browser/renderer_host/render_view_host.cc5
-rw-r--r--content/browser/renderer_host/render_view_host.h6
-rw-r--r--content/browser/renderer_host/render_widget_host.cc7
-rw-r--r--content/browser/renderer_host/render_widget_host.h5
-rw-r--r--content/browser/renderer_host/render_widget_host_view.h7
-rw-r--r--content/common/common_param_traits.cc19
-rw-r--r--content/common/common_param_traits.h12
-rw-r--r--content/common/font_descriptor_mac.h3
-rw-r--r--content/common/font_descriptor_mac.mm7
-rw-r--r--content/common/font_descriptor_mac_unittest.mm8
-rw-r--r--content/common/view_messages.h11
-rw-r--r--content/content_common.gypi1
-rw-r--r--content/renderer/render_view.cc26
-rw-r--r--content/renderer/render_widget.cc45
-rw-r--r--content/renderer/render_widget_fullscreen_pepper.cc4
-rw-r--r--ipc/ipc_message_utils.h1
41 files changed, 1296 insertions, 86 deletions
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1851177..f2328d2 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
#include "chrome/browser/renderer_host/chrome_render_view_host_observer.h"
+#include "chrome/browser/renderer_host/text_input_client_message_filter.h"
#include "chrome/browser/search_engines/search_provider_install_state_message_filter.h"
#include "chrome/browser/spellcheck_message_filter.h"
#include "chrome/browser/ui/webui/chrome_web_ui_factory.h"
@@ -61,6 +62,9 @@ void ChromeContentBrowserClient::BrowserRenderProcessHostCreated(
host->channel()->AddFilter(
new SearchProviderInstallStateMessageFilter(id, profile));
host->channel()->AddFilter(new SpellCheckMessageFilter(id));
+#if defined(OS_MACOSX)
+ host->channel()->AddFilter(new TextInputClientMessageFilter(host->id()));
+#endif
}
content::WebUIFactory* ChromeContentBrowserClient::GetWebUIFactory() {
diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
index 9a5a340..d4c9781a 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
@@ -830,7 +830,8 @@ void RenderWidgetHostViewGtk::SetTooltipText(const std::wstring& tooltip_text) {
}
}
-void RenderWidgetHostViewGtk::SelectionChanged(const std::string& text) {
+void RenderWidgetHostViewGtk::SelectionChanged(const std::string& text,
+ const ui::Range& range) {
if (!text.empty()) {
GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
gtk_clipboard_set_text(x_clipboard, text.c_str(), text.length());
diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.h b/chrome/browser/renderer_host/render_widget_host_view_gtk.h
index 0cf950a..d70dfbc 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_gtk.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.h
@@ -84,7 +84,8 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView,
virtual void Destroy();
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {}
virtual void SetTooltipText(const std::wstring& tooltip_text);
- virtual void SelectionChanged(const std::string& text);
+ virtual void SelectionChanged(const std::string& text,
+ const ui::Range& range);
virtual void ShowingContextMenu(bool showing);
virtual BackingStore* AllocBackingStore(const gfx::Size& size);
virtual void SetBackground(const SkBitmap& background);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h
index 048f402..576ce97 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h
@@ -73,12 +73,6 @@ class RWHVMEditCommandHelper;
// Represents the input-method attributes supported by this object.
scoped_nsobject<NSArray> validAttributesForMarkedText_;
- // Represents the cursor position in this view coordinate.
- // The renderer sends the cursor position through an IPC message.
- // We save the latest cursor position here and return it when an input
- // methods needs it.
- NSRect caretRect_;
-
// Indicates if we are currently handling a key down event.
BOOL handlingKeyDown_;
@@ -95,11 +89,7 @@ class RWHVMEditCommandHelper;
// the whole content yet.
NSRange markedRange_;
- // The selected range inside current marked text.
- // TODO(suzhe): Currently it's only valid when there is any marked text.
- // In the future, we may need to support accessing the whole content of the
- // DOM node being edited, then this should be the selected range inside the
- // DOM node.
+ // The selected range, cached from a message sent by the renderer.
NSRange selectedRange_;
// Text to be inserted which was generated by handling a key down event.
@@ -130,7 +120,7 @@ class RWHVMEditCommandHelper;
BOOL mouseEventWasIgnored_;
}
-@property(assign, nonatomic) NSRect caretRect;
+@property(nonatomic, readonly) NSRange selectedRange;
- (void)setCanBeKeyView:(BOOL)can;
- (void)setTakesFocusOnlyOnMouseDown:(BOOL)b;
@@ -206,6 +196,7 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
virtual void ImeUpdateTextInputState(WebKit::WebTextInputType state,
const gfx::Rect& caret_rect);
virtual void ImeCancelComposition();
+ virtual void ImeCompositionRangeChanged(const ui::Range& range);
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
@@ -214,7 +205,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {};
virtual void Destroy();
virtual void SetTooltipText(const std::wstring& tooltip_text);
- virtual void SelectionChanged(const std::string& text);
+ virtual void SelectionChanged(const std::string& text,
+ const ui::Range& range);
virtual BackingStore* AllocBackingStore(const gfx::Size& size);
virtual void SetTakesFocusOnlyOnMouseDown(bool flag);
// See comment in RenderWidgetHostView!
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index 14ac88f..e2eb64f 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -18,6 +18,7 @@
#import "chrome/browser/accessibility/browser_accessibility_cocoa.h"
#include "chrome/browser/accessibility/browser_accessibility_state.h"
#include "chrome/browser/browser_trial.h"
+#import "chrome/browser/renderer_host/text_input_client_mac.h"
#include "chrome/browser/spellchecker_platform_engine.h"
#import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h"
#import "chrome/browser/ui/cocoa/view_id_util.h"
@@ -43,6 +44,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "ui/gfx/gl/gl_switches.h"
+#include "ui/gfx/point.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/gfx/surface/io_surface_support_mac.h"
#include "webkit/glue/webaccessibility.h"
@@ -62,10 +64,14 @@ static inline int ToWebKitModifiers(NSUInteger flags) {
return modifiers;
}
-@interface RenderWidgetHostViewCocoa (Private)
+// Private methods:
+@interface RenderWidgetHostViewCocoa ()
+@property(nonatomic, assign) NSRange selectedRange;
+@property(nonatomic, assign) NSRange markedRange;
+
+ (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
-- (void)keyEvent:(NSEvent *)theEvent wasKeyEquivalent:(BOOL)equiv;
+- (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
- (void)cancelChildPopups;
- (void)checkForPluginImeCancellation;
@end
@@ -854,19 +860,19 @@ void RenderWidgetHostViewMac::ImeUpdateTextInputState(
if (HasFocus())
SetTextInputActive(true);
}
-
- // We need to convert the coordinate of the cursor rectangle sent from the
- // renderer and save it. Our input method backend uses a coordinate system
- // whose origin is the upper-left corner of this view. On the other hand,
- // Cocoa uses a coordinate system whose origin is the lower-left corner of
- // this view. So, we convert the cursor rectangle and save it.
- [cocoa_view_ setCaretRect:[cocoa_view_ flipRectToNSRect:caret_rect]];
}
void RenderWidgetHostViewMac::ImeCancelComposition() {
[cocoa_view_ cancelComposition];
}
+void RenderWidgetHostViewMac::ImeCompositionRangeChanged(
+ const ui::Range& range) {
+ // 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()];
+}
+
void RenderWidgetHostViewMac::DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects) {
@@ -983,8 +989,10 @@ void RenderWidgetHostViewMac::SetTooltipText(const std::wstring& tooltip_text) {
// RenderWidgetHostViewCocoa uses the stored selection text,
// which implements NSServicesRequests protocol.
//
-void RenderWidgetHostViewMac::SelectionChanged(const std::string& text) {
+void RenderWidgetHostViewMac::SelectionChanged(const std::string& text,
+ const ui::Range& range) {
selected_text_ = text;
+ [cocoa_view_ setSelectedRange:range.ToNSRange()];
}
bool RenderWidgetHostViewMac::IsPopup() const {
@@ -1386,7 +1394,8 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
@implementation RenderWidgetHostViewCocoa
-@synthesize caretRect = caretRect_;
+@synthesize selectedRange = selectedRange_;
+@synthesize markedRange = markedRange_;
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
self = [super initWithFrame:NSZeroRect];
@@ -2511,31 +2520,34 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
- NOTIMPLEMENTED();
- return NSNotFound;
+ DCHECK([self window]);
+ // |thePoint| is in screen coordinates, but needs to be converted to WebKit
+ // coordinates (upper left origin). Scroll offsets will be taken care of in
+ // the renderer.
+ thePoint = [[self window] convertScreenToBase:thePoint];
+ thePoint = [self convertPoint:thePoint fromView:nil];
+ thePoint.y = NSHeight([self frame]) - thePoint.y;
+
+ NSUInteger point =
+ TextInputClientMac::GetInstance()->GetCharacterIndexAtPoint(
+ renderWidgetHostView_->render_widget_host_,
+ gfx::Point(thePoint.x, thePoint.y));
+ return point;
}
- (NSRect)firstRectForCharacterRange:(NSRange)theRange {
- // An input method requests a cursor rectangle to display its candidate
- // window.
- // Calculate the screen coordinate of the cursor rectangle saved in
- // RenderWidgetHostViewMac::ImeUpdateTextInputState() and send it to the
- // input method.
- // Since this window may be moved since we receive the cursor rectangle last
- // time we sent the cursor rectangle to the input method, so we should map
- // from the view coordinate to the screen coordinate every time when an input
- // method need it.
- NSRect resultRect = [self convertRect:caretRect_ toView:nil];
- NSWindow* window = [self window];
- if (window)
- resultRect.origin = [window convertBaseToScreen:resultRect.origin];
-
- return resultRect;
-}
+ NSRect rect = TextInputClientMac::GetInstance()->GetFirstRectForRange(
+ renderWidgetHostView_->render_widget_host_, theRange);
-- (NSRange)selectedRange {
- // Return the selected range saved in the setMarkedText method.
- return hasMarkedText_ ? selectedRange_ : NSMakeRange(NSNotFound, 0);
+ // The returned rectangle is in WebKit coordinates (upper left origin), so
+ // flip the coordinate system and then convert it into screen coordinates for
+ // return.
+ NSRect viewFrame = [self frame];
+ rect.origin.y = NSHeight(viewFrame) - rect.origin.y;
+ rect.origin.y -= rect.size.height;
+ rect = [self convertRectToBase:rect];
+ rect.origin = [[self window] convertBaseToScreen:rect.origin];
+ return rect;
}
- (NSRange)markedRange {
@@ -2548,12 +2560,11 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
}
-- (NSAttributedString *)attributedSubstringFromRange:(NSRange)range {
- // TODO(hbono): Even though many input method works without implementing
- // this method, we need to save a copy of the string in the setMarkedText
- // method and create a NSAttributedString with the given range.
- // http://crbug.com/37715
- return nil;
+- (NSAttributedString*)attributedSubstringFromRange:(NSRange)range {
+ NSAttributedString* str =
+ TextInputClientMac::GetInstance()->GetAttributedSubstringFromRange(
+ renderWidgetHostView_->render_widget_host_, range);
+ return str;
}
- (NSInteger)conversationIdentifier {
@@ -2612,7 +2623,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
NSString* im_text = isAttributedString ? [string string] : string;
int length = [im_text length];
- markedRange_ = NSMakeRange(0, length);
+ // |markedRange_| will get set on a callback from ImeSetComposition().
selectedRange_ = newSelRange;
markedText_ = base::SysNSStringToUTF16(im_text);
hasMarkedText_ = (length > 0);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.cc b/chrome/browser/renderer_host/render_widget_host_view_views.cc
index ee2c43f..be7e8fc 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.cc
@@ -355,7 +355,8 @@ void RenderWidgetHostViewViews::SetTooltipText(const std::wstring& tip) {
// NOTIMPLEMENTED(); ... too annoying, it triggers for every mousemove
}
-void RenderWidgetHostViewViews::SelectionChanged(const std::string& text) {
+void RenderWidgetHostViewViews::SelectionChanged(const std::string& text,
+ const ui::Range& range) {
// TODO(anicolao): deal with the clipboard without GTK
NOTIMPLEMENTED();
}
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.h b/chrome/browser/renderer_host/render_widget_host_view_views.h
index 177f50b..955afa9 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.h
@@ -70,7 +70,8 @@ class RenderWidgetHostViewViews : public RenderWidgetHostView,
virtual void Destroy() OVERRIDE;
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) OVERRIDE {}
virtual void SetTooltipText(const std::wstring& tooltip_text) OVERRIDE;
- virtual void SelectionChanged(const std::string& text) OVERRIDE;
+ virtual void SelectionChanged(const std::string& text,
+ const ui::Range& range) OVERRIDE;
virtual void ShowingContextMenu(bool showing) OVERRIDE;
virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE;
virtual void SetBackground(const SkBitmap& background) OVERRIDE;
diff --git a/chrome/browser/renderer_host/text_input_client_mac.h b/chrome/browser/renderer_host/text_input_client_mac.h
new file mode 100644
index 0000000..00c2e29
--- /dev/null
+++ b/chrome/browser/renderer_host/text_input_client_mac.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_TEXT_INPUT_CLIENT_MAC_H_
+#define CHROME_BROWSER_RENDERER_HOST_TEXT_INPUT_CLIENT_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/memory/scoped_nsobject.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "ui/gfx/point.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+class RenderWidgetHost;
+
+// This class helps with the Mac OS X dictionary popup. For the design overview,
+// look at this document:
+// http://dev.chromium.org/developers/design-documents/system-dictionary-pop-up-architecture
+//
+// This service is used to marshall information for these three methods that are
+// implemented in RenderWidgetHostViewMac:
+// -[NSTextInput characterIndexForPoint:]
+// -[NSTextInput attributedSubstringFromRange:]
+// -[NSTextInput firstRectForCharacterRange:]
+//
+// Because these methods are part of a synchronous system API, implementing them
+// requires getting information from the renderer synchronously. Rather than
+// using an actual sync IPC message, a normal async ViewMsg is used with a lock
+// and condition (managed by this service).
+class TextInputClientMac {
+ public:
+ // Returns the singleton instance.
+ static TextInputClientMac* GetInstance();
+
+ // Each of the three methods mentioned above has an associated pair of methods
+ // to get data from the renderer. The Get*() methods block the calling thread
+ // (always the UI thread) with a short timeout after the async message has
+ // been sent to the renderer to lookup the information needed to respond to
+ // the system. The Set*AndSignal() methods store the looked up information in
+ // this service and signal the condition to allow the Get*() methods to
+ // unlock and return that stored value.
+ //
+ // Returns NSNotFound if the request times out or is not completed.
+ NSUInteger GetCharacterIndexAtPoint(RenderWidgetHost* rwh, gfx::Point point);
+ // Returns nil if the request times out or is completed.
+ NSAttributedString* GetAttributedSubstringFromRange(RenderWidgetHost* rwh,
+ NSRange range);
+ // Returns NSZeroRect if the request times out or is not completed. The result
+ // is in WebKit coordinates.
+ NSRect GetFirstRectForRange(RenderWidgetHost* rwh, NSRange range);
+
+ // When the renderer sends the ViewHostMsg reply, the RenderMessageFilter will
+ // call the corresponding method on the IO thread to unlock the condition and
+ // allow the Get*() methods to continue/return.
+ void SetCharacterIndexAndSignal(NSUInteger index);
+ void SetFirstRectAndSignal(NSRect first_rect);
+ void SetSubstringAndSignal(NSAttributedString* string);
+
+ private:
+ friend struct DefaultSingletonTraits<TextInputClientMac>;
+ TextInputClientMac();
+ ~TextInputClientMac();
+
+ // The critical sections that the Condition guards are in Get*() methods.
+ // These methods lock the internal condition for use before the asynchronous
+ // message is sent to the renderer to lookup the required information. These
+ // are only used on the UI thread.
+ void BeforeRequest();
+ // Called at the end of a critical section. This will release the lock and
+ // condition.
+ void AfterRequest();
+
+ NSUInteger character_index_;
+ NSRect first_rect_;
+ scoped_nsobject<NSAttributedString> substring_;
+
+ base::Lock lock_;
+ base::ConditionVariable condition_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextInputClientMac);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_TEXT_INPUT_CLIENT_MAC_H_
diff --git a/chrome/browser/renderer_host/text_input_client_mac.mm b/chrome/browser/renderer_host/text_input_client_mac.mm
new file mode 100644
index 0000000..f78f8b3
--- /dev/null
+++ b/chrome/browser/renderer_host/text_input_client_mac.mm
@@ -0,0 +1,122 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/renderer_host/text_input_client_mac.h"
+
+#include "base/memory/singleton.h"
+#include "base/metrics/histogram.h"
+#include "base/time.h"
+#include "chrome/common/text_input_client_messages.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+
+// The amount of time in milliseconds that the browser process will wait for a
+// response from the renderer.
+// TODO(rsesek): Using the histogram data, find the best upper-bound for this
+// value.
+const float kWaitTimeout = 1500;
+
+TextInputClientMac::TextInputClientMac()
+ : character_index_(NSNotFound),
+ lock_(),
+ condition_(&lock_) {
+}
+
+TextInputClientMac::~TextInputClientMac() {
+}
+
+// static
+TextInputClientMac* TextInputClientMac::GetInstance() {
+ return Singleton<TextInputClientMac>::get();
+}
+
+NSUInteger TextInputClientMac::GetCharacterIndexAtPoint(RenderWidgetHost* rwh,
+ gfx::Point point) {
+ base::TimeTicks start = base::TimeTicks::Now();
+
+ BeforeRequest();
+ rwh->Send(new TextInputClientMsg_CharacterIndexForPoint(rwh->routing_id(),
+ point));
+ condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout));
+ AfterRequest();
+
+ base::TimeDelta delta(base::TimeTicks::Now() - start);
+ UMA_HISTOGRAM_TIMES("TextInputClient.CharacterIndex",
+ delta * base::Time::kMicrosecondsPerMillisecond);
+
+ return character_index_;
+}
+
+NSRect TextInputClientMac::GetFirstRectForRange(RenderWidgetHost* rwh,
+ NSRange range) {
+ base::TimeTicks start = base::TimeTicks::Now();
+
+ BeforeRequest();
+ rwh->Send(new TextInputClientMsg_FirstRectForCharacterRange(rwh->routing_id(),
+ ui::Range(range)));
+ condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout));
+ AfterRequest();
+
+ base::TimeDelta delta(base::TimeTicks::Now() - start);
+ UMA_HISTOGRAM_TIMES("TextInputClient.FirstRect",
+ delta * base::Time::kMicrosecondsPerMillisecond);
+
+ return first_rect_;
+}
+
+NSAttributedString* TextInputClientMac::GetAttributedSubstringFromRange(
+ RenderWidgetHost* rwh,
+ NSRange range) {
+ base::TimeTicks start = base::TimeTicks::Now();
+
+ BeforeRequest();
+ rwh->Send(new TextInputClientMsg_StringForRange(rwh->routing_id(),
+ ui::Range(range)));
+ condition_.TimedWait(base::TimeDelta::FromMilliseconds(kWaitTimeout));
+ AfterRequest();
+
+ base::TimeDelta delta(base::TimeTicks::Now() - start);
+ UMA_HISTOGRAM_TIMES("TextInputClient.Substring",
+ delta * base::Time::kMicrosecondsPerMillisecond);
+
+ return substring_.get();
+}
+
+void TextInputClientMac::SetCharacterIndexAndSignal(NSUInteger index) {
+ lock_.Acquire();
+ character_index_ = index;
+ lock_.Release();
+ condition_.Signal();
+}
+
+void TextInputClientMac::SetFirstRectAndSignal(NSRect first_rect) {
+ lock_.Acquire();
+ first_rect_ = first_rect;
+ lock_.Release();
+ condition_.Signal();
+}
+
+void TextInputClientMac::SetSubstringAndSignal(NSAttributedString* string) {
+ lock_.Acquire();
+ substring_.reset([string copy]);
+ lock_.Release();
+ condition_.Signal();
+}
+
+void TextInputClientMac::BeforeRequest() {
+ base::TimeTicks start = base::TimeTicks::Now();
+
+ lock_.Acquire();
+
+ base::TimeDelta delta(base::TimeTicks::Now() - start);
+ UMA_HISTOGRAM_TIMES("TextInputClient.LockWait",
+ delta * base::Time::kMicrosecondsPerMillisecond);
+
+ character_index_ = NSNotFound;
+ first_rect_ = NSZeroRect;
+ substring_.reset();
+}
+
+void TextInputClientMac::AfterRequest() {
+ lock_.Release();
+}
diff --git a/chrome/browser/renderer_host/text_input_client_mac_unittest.mm b/chrome/browser/renderer_host/text_input_client_mac_unittest.mm
new file mode 100644
index 0000000..8d1c794
--- /dev/null
+++ b/chrome/browser/renderer_host/text_input_client_mac_unittest.mm
@@ -0,0 +1,160 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/renderer_host/text_input_client_mac.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/threading/thread.h"
+#include "chrome/common/text_input_client_messages.h"
+#include "chrome/test/testing_profile.h"
+#include "content/browser/renderer_host/mock_render_process_host.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+namespace {
+
+// This test does not test the WebKit side of the dictionary system (which
+// performs the actual data fetching), but rather this just tests that the
+// service's signaling system works.
+class TextInputClientMacTest : public testing::Test {
+ public:
+ TextInputClientMacTest()
+ : message_loop_(MessageLoop::TYPE_UI),
+ profile_(),
+ process_(new MockRenderProcessHost(&profile_)),
+ widget_(process_, 1),
+ thread_("TextInputClientMacTestThread") {}
+
+ // Accessor for the TextInputClientMac instance.
+ TextInputClientMac* service() {
+ return TextInputClientMac::GetInstance();
+ }
+
+ // Helper method to post a task on the testing thread's MessageLoop after
+ // a short delay.
+ void PostTask(const base::Closure& task) {
+ const int64 kTaskDelayMs = 200;
+ thread_.message_loop()->PostDelayedTask(FROM_HERE, task, kTaskDelayMs);
+ }
+
+ RenderWidgetHost* widget() {
+ return &widget_;
+ }
+
+ IPC::TestSink& ipc_sink() {
+ return process_->sink();
+ }
+
+ private:
+ friend class ScopedTestingThread;
+
+ MessageLoop message_loop_;
+ TestingProfile profile_;
+
+ // Gets deleted when the last RWH in the "process" gets destroyed.
+ MockRenderProcessHost* process_;
+ RenderWidgetHost widget_;
+
+ base::Thread thread_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Helper class that Start()s and Stop()s a thread according to the scope of the
+// object.
+class ScopedTestingThread {
+ public:
+ ScopedTestingThread(TextInputClientMacTest* test) : thread_(test->thread_) {
+ thread_.Start();
+ }
+ ~ScopedTestingThread() {
+ thread_.Stop();
+ }
+
+ private:
+ base::Thread& thread_;
+};
+
+// Test Cases //////////////////////////////////////////////////////////////////
+
+TEST_F(TextInputClientMacTest, GetCharacterIndex) {
+ ScopedTestingThread thread(this);
+ const NSUInteger kSuccessValue = 42;
+
+ PostTask(base::Bind(&TextInputClientMac::SetCharacterIndexAndSignal,
+ base::Unretained(service()), kSuccessValue));
+ NSUInteger index = service()->GetCharacterIndexAtPoint(
+ widget(), gfx::Point(2, 2));
+
+ EXPECT_EQ(1U, ipc_sink().message_count());
+ EXPECT_TRUE(ipc_sink().GetUniqueMessageMatching(
+ TextInputClientMsg_CharacterIndexForPoint::ID));
+ EXPECT_EQ(kSuccessValue, index);
+}
+
+TEST_F(TextInputClientMacTest, TimeoutCharacterIndex) {
+ NSUInteger index = service()->GetCharacterIndexAtPoint(
+ widget(), gfx::Point(2, 2));
+ EXPECT_EQ(1U, ipc_sink().message_count());
+ EXPECT_TRUE(ipc_sink().GetUniqueMessageMatching(
+ TextInputClientMsg_CharacterIndexForPoint::ID));
+ EXPECT_EQ(NSNotFound, index);
+}
+
+TEST_F(TextInputClientMacTest, GetRectForRange) {
+ ScopedTestingThread thread(this);
+ const NSRect kSuccessValue = NSMakeRect(42, 43, 44, 45);
+
+ PostTask(base::Bind(&TextInputClientMac::SetFirstRectAndSignal,
+ base::Unretained(service()), kSuccessValue));
+ NSRect rect = service()->GetFirstRectForRange(widget(), NSMakeRange(0, 32));
+
+ EXPECT_EQ(1U, ipc_sink().message_count());
+ EXPECT_TRUE(ipc_sink().GetUniqueMessageMatching(
+ TextInputClientMsg_FirstRectForCharacterRange::ID));
+ EXPECT_TRUE(NSEqualRects(kSuccessValue, rect));
+}
+
+TEST_F(TextInputClientMacTest, TimeoutRectForRange) {
+ NSRect rect = service()->GetFirstRectForRange(widget(), NSMakeRange(0, 32));
+ EXPECT_EQ(1U, ipc_sink().message_count());
+ EXPECT_TRUE(ipc_sink().GetUniqueMessageMatching(
+ TextInputClientMsg_FirstRectForCharacterRange::ID));
+ EXPECT_TRUE(NSEqualRects(NSZeroRect, rect));
+}
+
+TEST_F(TextInputClientMacTest, GetSubstring) {
+ ScopedTestingThread thread(this);
+ NSDictionary* attributes =
+ [NSDictionary dictionaryWithObject:[NSColor purpleColor]
+ forKey:NSForegroundColorAttributeName];
+ scoped_nsobject<NSAttributedString> kSuccessValue(
+ [[NSAttributedString alloc] initWithString:@"Barney is a purple dinosaur"
+ attributes:attributes]);
+
+ PostTask(base::Bind(&TextInputClientMac::SetSubstringAndSignal,
+ base::Unretained(service()), base::Unretained(kSuccessValue.get())));
+ NSAttributedString* string = service()->GetAttributedSubstringFromRange(
+ widget(), NSMakeRange(0, 32));
+
+ EXPECT_NSEQ(kSuccessValue, string);
+ EXPECT_NE(kSuccessValue.get(), string); // |string| should be a copy.
+ EXPECT_EQ(1U, ipc_sink().message_count());
+ EXPECT_TRUE(ipc_sink().GetUniqueMessageMatching(
+ TextInputClientMsg_StringForRange::ID));
+}
+
+TEST_F(TextInputClientMacTest, TimeoutSubstring) {
+ NSAttributedString* string = service()->GetAttributedSubstringFromRange(
+ widget(), NSMakeRange(0, 32));
+ EXPECT_EQ(nil, string);
+ EXPECT_EQ(1U, ipc_sink().message_count());
+ EXPECT_TRUE(ipc_sink().GetUniqueMessageMatching(
+ TextInputClientMsg_StringForRange::ID));
+}
+
+} // namespace
diff --git a/chrome/browser/renderer_host/text_input_client_message_filter.h b/chrome/browser/renderer_host/text_input_client_message_filter.h
new file mode 100644
index 0000000..17c022b
--- /dev/null
+++ b/chrome/browser/renderer_host/text_input_client_message_filter.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_RENDERER_HOST_TEXT_INPUT_CLIENT_MESSAGE_FILTER_H_
+#define CHROME_BROWSER_RENDERER_HOST_TEXT_INPUT_CLIENT_MESSAGE_FILTER_H_
+
+#include "chrome/common/attributed_string_coder_mac.h"
+#include "content/browser/browser_message_filter.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+class Range;
+}
+
+// This is a browser-side message filter that lives on the IO thread to handle
+// replies to messages sent by the TextInputClientMac. See
+// chrome/browser/renderer_host/text_input_client_mac.h for more information.
+class TextInputClientMessageFilter : public BrowserMessageFilter {
+ public:
+ explicit TextInputClientMessageFilter(int child_id);
+ virtual ~TextInputClientMessageFilter();
+
+ // BrowserMessageFilter override:
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ private:
+ // IPC Message handlers:
+ void OnGotCharacterIndexForPoint(size_t index);
+ void OnGotFirstRectForRange(const gfx::Rect& rect);
+ void OnGotStringFromRange(
+ const mac::AttributedStringCoder::EncodedString& string);
+
+ // Child process id.
+ int child_process_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextInputClientMessageFilter);
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_TEXT_INPUT_CLIENT_MESSAGE_FILTER_H_
diff --git a/chrome/browser/renderer_host/text_input_client_message_filter.mm b/chrome/browser/renderer_host/text_input_client_message_filter.mm
new file mode 100644
index 0000000..7bfbdfc
--- /dev/null
+++ b/chrome/browser/renderer_host/text_input_client_message_filter.mm
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/renderer_host/text_input_client_message_filter.h"
+
+#include "base/memory/scoped_nsobject.h"
+#include "base/string16.h"
+#include "chrome/browser/renderer_host/text_input_client_mac.h"
+#include "chrome/common/attributed_string_coder_mac.h"
+#include "chrome/common/text_input_client_messages.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/render_widget_host_view.h"
+#include "ipc/ipc_message_macros.h"
+#include "ui/base/range/range.h"
+#include "ui/gfx/rect.h"
+
+TextInputClientMessageFilter::TextInputClientMessageFilter(int child_id)
+ : BrowserMessageFilter(),
+ child_process_id_(child_id) {
+}
+
+TextInputClientMessageFilter::~TextInputClientMessageFilter() {
+}
+
+bool TextInputClientMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(TextInputClientMessageFilter, message,
+ *message_was_ok)
+ IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotCharacterIndexForPoint,
+ OnGotCharacterIndexForPoint)
+ IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotFirstRectForRange,
+ OnGotFirstRectForRange)
+ IPC_MESSAGE_HANDLER(TextInputClientReplyMsg_GotStringForRange,
+ OnGotStringFromRange)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void TextInputClientMessageFilter::OnGotCharacterIndexForPoint(size_t index) {
+ TextInputClientMac* service = TextInputClientMac::GetInstance();
+ service->SetCharacterIndexAndSignal(index);
+}
+
+void TextInputClientMessageFilter::OnGotFirstRectForRange(
+ const gfx::Rect& rect) {
+ TextInputClientMac* service = TextInputClientMac::GetInstance();
+ service->SetFirstRectAndSignal(NSRectFromCGRect(rect.ToCGRect()));
+}
+
+void TextInputClientMessageFilter::OnGotStringFromRange(
+ const mac::AttributedStringCoder::EncodedString& encoded_string) {
+ TextInputClientMac* service = TextInputClientMac::GetInstance();
+ NSAttributedString* string =
+ mac::AttributedStringCoder::Decode(&encoded_string);
+ if (![string length])
+ string = nil;
+ service->SetSubstringAndSignal(string);
+}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index bb70bd0..2613bbb 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1718,6 +1718,10 @@
'browser/renderer_host/safe_browsing_resource_handler.h',
'browser/renderer_host/save_file_resource_handler.cc',
'browser/renderer_host/save_file_resource_handler.h',
+ 'browser/renderer_host/text_input_client_mac.h',
+ 'browser/renderer_host/text_input_client_mac.mm',
+ 'browser/renderer_host/text_input_client_message_filter.h',
+ 'browser/renderer_host/text_input_client_message_filter.mm',
'browser/renderer_host/web_cache_manager.cc',
'browser/renderer_host/web_cache_manager.h',
'browser/renderer_preferences_util.cc',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 942d4ee..451402d6 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -28,6 +28,8 @@
'common/app_mode_common_mac.h',
'common/app_mode_common_mac.mm',
'common/attrition_experiments.h',
+ 'common/attributed_string_coder_mac.h',
+ 'common/attributed_string_coder_mac.mm',
'common/auto_start_linux.cc',
'common/auto_start_linux.h',
'common/autofill_messages.h',
@@ -228,6 +230,8 @@
'common/spellcheck_messages.h',
'common/sqlite_utils.cc',
'common/sqlite_utils.h',
+ 'common/text_input_client_messages.cc',
+ 'common/text_input_client_messages.h',
'common/thumbnail_score.cc',
'common/thumbnail_score.h',
'common/url_constants.cc',
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 043e487..7c43d77 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -168,6 +168,8 @@
'renderer/spellchecker/spellcheck_worditerator.h',
'renderer/translate_helper.cc',
'renderer/translate_helper.h',
+ 'renderer/text_input_client_observer.cc',
+ 'renderer/text_input_client_observer.h',
'renderer/visitedlink_slave.cc',
'renderer/visitedlink_slave.h',
],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 6bbfd4d..31514a2 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1476,6 +1476,7 @@
'browser/remoting/directory_add_request_unittest.cc',
'browser/renderer_host/gtk_key_bindings_handler_unittest.cc',
'browser/renderer_host/render_widget_host_view_mac_unittest.mm',
+ 'browser/renderer_host/text_input_client_mac_unittest.mm',
'browser/renderer_host/web_cache_manager_unittest.cc',
'browser/resources_util_unittest.cc',
'browser/rlz/rlz_unittest.cc',
@@ -1787,6 +1788,7 @@
'browser/webdata/web_data_service_test_util.h',
'browser/webdata/web_data_service_unittest.cc',
'browser/webdata/web_database_migration_unittest.cc',
+ 'common/attributed_string_coder_mac_unittest.mm',
'common/bzip2_unittest.cc',
'common/child_process_logging_mac_unittest.mm',
'common/chrome_paths_unittest.cc',
diff --git a/chrome/common/attributed_string_coder_mac.h b/chrome/common/attributed_string_coder_mac.h
new file mode 100644
index 0000000..4ac02eb
--- /dev/null
+++ b/chrome/common/attributed_string_coder_mac.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_ATTRIBUTED_STRING_CODER_MAC_H_
+#define CHROME_COMMON_ATTRIBUTED_STRING_CODER_MAC_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "content/common/font_descriptor_mac.h"
+#include "ipc/ipc_message_utils.h"
+#include "ui/base/range/range.h"
+
+#if __OBJC__
+@class NSAttributedString;
+@class NSDictionary;
+#else
+class NSAttributedString;
+class NSDictionary;
+#endif
+
+namespace mac {
+
+// This class will serialize the font information of an NSAttributedString so
+// that it can be sent over IPC. This class only stores the information of the
+// NSFontAttributeName. The motive is that of security: using NSArchiver and
+// friends to send objects from the renderer to the browser could lead to
+// deserialization of arbitrary objects. This class restricts serialization to
+// a specific object class and specific attributes of that object.
+class AttributedStringCoder {
+ public:
+ // A C++ IPC-friendly representation of the NSFontAttributeName attribute
+ // set.
+ class FontAttribute {
+ public:
+ FontAttribute(NSDictionary* ns_attributes, ui::Range effective_range);
+ FontAttribute(FontDescriptor font, ui::Range range);
+ FontAttribute();
+ ~FontAttribute();
+
+ // Creates an autoreleased NSDictionary that can be attached to an
+ // NSAttributedString.
+ NSDictionary* ToAttributesDictionary() const;
+
+ // Whether or not the attribute should be placed in the EncodedString. This
+ // can return false, e.g. if the Cocoa-based constructor can't find any
+ // information to encode.
+ bool ShouldEncode() const;
+
+ // Accessors:
+ FontDescriptor font_descriptor() const { return font_descriptor_; }
+ ui::Range effective_range() const { return effective_range_; }
+
+ private:
+ FontDescriptor font_descriptor_;
+ ui::Range effective_range_;
+ };
+
+ // A class that contains the pertinent information from an NSAttributedString,
+ // which can be serialized over IPC.
+ class EncodedString {
+ public:
+ explicit EncodedString(string16 string);
+ EncodedString();
+ ~EncodedString();
+
+ // Accessors:
+ string16 string() const { return string_; }
+ const std::vector<FontAttribute>& attributes() const {
+ return attributes_;
+ }
+ std::vector<FontAttribute>* attributes() { return &attributes_; }
+
+ private:
+ // The plain-text string.
+ string16 string_;
+ // The set of attributes that style |string_|.
+ std::vector<FontAttribute> attributes_;
+ };
+
+ // Takes an NSAttributedString, extracts the pertinent attributes, and returns
+ // an object that represents it. Caller owns the result.
+ static const EncodedString* Encode(NSAttributedString* str);
+
+ // Returns an autoreleased NSAttributedString from an encoded representation.
+ static NSAttributedString* Decode(const EncodedString* str);
+
+ private:
+ AttributedStringCoder();
+};
+
+} // namespace mac
+
+// IPC ParamTraits specialization //////////////////////////////////////////////
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mac::AttributedStringCoder::EncodedString> {
+ typedef mac::AttributedStringCoder::EncodedString param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::string* l);
+};
+
+template <>
+struct ParamTraits<mac::AttributedStringCoder::FontAttribute> {
+ typedef mac::AttributedStringCoder::FontAttribute param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::string* l);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_ATTRIBUTED_STRING_CODER_MAC_H_
diff --git a/chrome/common/attributed_string_coder_mac.mm b/chrome/common/attributed_string_coder_mac.mm
new file mode 100644
index 0000000..793d3fb
--- /dev/null
+++ b/chrome/common/attributed_string_coder_mac.mm
@@ -0,0 +1,171 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/attributed_string_coder_mac.h"
+
+#include <AppKit/AppKit.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_nsobject.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "content/common/common_param_traits.h"
+#include "content/common/view_messages.h"
+#include "ipc/ipc_message_utils.h"
+
+namespace mac {
+
+// static
+const AttributedStringCoder::EncodedString* AttributedStringCoder::Encode(
+ NSAttributedString* str) {
+ // Create the return value.
+ EncodedString* encoded_string =
+ new EncodedString(base::SysNSStringToUTF16([str string]));
+ // Iterate over all the attributes in the string.
+ NSUInteger length = [str length];
+ for (NSUInteger i = 0; i < length; ) {
+ NSRange effective_range;
+ NSDictionary* ns_attributes = [str attributesAtIndex:i
+ effectiveRange:&effective_range];
+ // Convert the attributes to IPC-friendly types.
+ FontAttribute attrs(ns_attributes, ui::Range(effective_range));
+ // Only encode the attributes if the filtered set contains font information.
+ if (attrs.ShouldEncode()) {
+ encoded_string->attributes()->push_back(attrs);
+ }
+ // Advance the iterator to the position outside of the effective range.
+ i = NSMaxRange(effective_range);
+ }
+ return encoded_string;
+}
+
+// static
+NSAttributedString* AttributedStringCoder::Decode(
+ const AttributedStringCoder::EncodedString* str) {
+ // Create the return value.
+ NSString* plain_text = base::SysUTF16ToNSString(str->string());
+ scoped_nsobject<NSMutableAttributedString> decoded_string(
+ [[NSMutableAttributedString alloc] initWithString:plain_text]);
+ // Iterate over all the encoded attributes, attaching each to the string.
+ const std::vector<FontAttribute> attributes = str->attributes();
+ for (std::vector<FontAttribute>::const_iterator it = attributes.begin();
+ it != attributes.end(); ++it) {
+ // Protect against ranges that are outside the range of the string.
+ const ui::Range& range = it->effective_range();
+ if (range.GetMin() > [plain_text length] ||
+ range.GetMax() > [plain_text length]) {
+ continue;
+ }
+ [decoded_string addAttributes:it->ToAttributesDictionary()
+ range:range.ToNSRange()];
+ }
+ return [decoded_string.release() autorelease];
+}
+
+// Data Types //////////////////////////////////////////////////////////////////
+
+AttributedStringCoder::EncodedString::EncodedString(string16 string)
+ : string_(string) {
+}
+
+AttributedStringCoder::EncodedString::EncodedString()
+ : string_() {
+}
+
+AttributedStringCoder::EncodedString::~EncodedString() {
+}
+
+AttributedStringCoder::FontAttribute::FontAttribute(NSDictionary* dict,
+ ui::Range effective_range)
+ : font_descriptor_(),
+ effective_range_(effective_range) {
+ NSFont* font = [dict objectForKey:NSFontAttributeName];
+ if (font) {
+ font_descriptor_ = FontDescriptor(font);
+ }
+}
+
+AttributedStringCoder::FontAttribute::FontAttribute(FontDescriptor font,
+ ui::Range range)
+ : font_descriptor_(font),
+ effective_range_(range) {
+}
+
+AttributedStringCoder::FontAttribute::FontAttribute()
+ : font_descriptor_(),
+ effective_range_() {
+}
+
+AttributedStringCoder::FontAttribute::~FontAttribute() {
+}
+
+NSDictionary* AttributedStringCoder::FontAttribute::ToAttributesDictionary(
+ void) const {
+ DCHECK(ShouldEncode());
+ NSFont* font = font_descriptor_.ToNSFont();
+ return [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
+}
+
+bool AttributedStringCoder::FontAttribute::ShouldEncode() const {
+ return !font_descriptor_.font_name.empty();
+}
+
+} // namespace mac
+
+// IPC ParamTraits specialization //////////////////////////////////////////////
+
+namespace IPC {
+
+using mac::AttributedStringCoder;
+
+void ParamTraits<AttributedStringCoder::EncodedString>::Write(
+ Message* m, const param_type& p) {
+ WriteParam(m, p.string());
+ WriteParam(m, p.attributes());
+}
+
+bool ParamTraits<AttributedStringCoder::EncodedString>::Read(
+ const Message* m, void** iter, param_type* p) {
+ bool success = true;
+
+ string16 result;
+ success &= ReadParam(m, iter, &result);
+ *p = AttributedStringCoder::EncodedString(result);
+
+ success &= ReadParam(m, iter, p->attributes());
+ return success;
+}
+
+void ParamTraits<AttributedStringCoder::EncodedString>::Log(
+ const param_type& p, std::string* l) {
+ l->append(UTF16ToUTF8(p.string()));
+}
+
+void ParamTraits<AttributedStringCoder::FontAttribute>::Write(
+ Message* m, const param_type& p) {
+ WriteParam(m, p.font_descriptor());
+ WriteParam(m, p.effective_range());
+}
+
+bool ParamTraits<AttributedStringCoder::FontAttribute>::Read(
+ const Message* m, void** iter, param_type* p) {
+ bool success = true;
+
+ FontDescriptor font;
+ success &= ReadParam(m, iter, &font);
+
+ ui::Range range;
+ success &= ReadParam(m, iter, &range);
+
+ if (success) {
+ *p = AttributedStringCoder::FontAttribute(font, range);
+ }
+ return success;
+}
+
+void ParamTraits<AttributedStringCoder::FontAttribute>::Log(
+ const param_type& p, std::string* l) {
+}
+
+} // namespace IPC
diff --git a/chrome/common/attributed_string_coder_mac_unittest.mm b/chrome/common/attributed_string_coder_mac_unittest.mm
new file mode 100644
index 0000000..bf7ebff
--- /dev/null
+++ b/chrome/common/attributed_string_coder_mac_unittest.mm
@@ -0,0 +1,135 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <AppKit/AppKit.h>
+
+#include "base/memory/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#import "chrome/common/attributed_string_coder_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+using mac::AttributedStringCoder;
+
+class AttributedStringCoderTest : public testing::Test {
+ public:
+ NSMutableAttributedString* NewAttrString() {
+ NSString* str = @"The quick brown fox jumped over the lazy dog.";
+ return [[NSMutableAttributedString alloc] initWithString:str];
+ }
+
+ NSDictionary* FontAttribute(NSString* name, CGFloat size) {
+ NSFont* font = [NSFont fontWithName:name size:size];
+ return [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
+ }
+
+ NSAttributedString* EncodeAndDecode(NSAttributedString* str) {
+ scoped_ptr<const AttributedStringCoder::EncodedString> encoded_str(
+ AttributedStringCoder::Encode(str));
+ return AttributedStringCoder::Decode(encoded_str.get());
+ }
+};
+
+TEST_F(AttributedStringCoderTest, SimpleString) {
+ scoped_nsobject<NSMutableAttributedString> attr_str(NewAttrString());
+ [attr_str addAttributes:FontAttribute(@"Helvetica", 12.5)
+ range:NSMakeRange(0, [attr_str length])];
+
+ NSAttributedString* decoded = EncodeAndDecode(attr_str.get());
+ EXPECT_NSEQ(attr_str.get(), decoded);
+}
+
+TEST_F(AttributedStringCoderTest, NoAttributes) {
+ scoped_nsobject<NSAttributedString> attr_str(NewAttrString());
+ NSAttributedString* decoded = EncodeAndDecode(attr_str.get());
+ EXPECT_NSEQ(attr_str.get(), decoded);
+}
+
+TEST_F(AttributedStringCoderTest, StripColor) {
+ scoped_nsobject<NSMutableAttributedString> attr_str(NewAttrString());
+ const NSUInteger kStringLength = [attr_str length];
+ [attr_str addAttribute:NSFontAttributeName
+ value:[NSFont systemFontOfSize:26]
+ range:NSMakeRange(0, kStringLength)];
+ [attr_str addAttribute:NSForegroundColorAttributeName
+ value:[NSColor redColor]
+ range:NSMakeRange(0, kStringLength)];
+
+ NSAttributedString* decoded = EncodeAndDecode(attr_str.get());
+
+ NSRange range;
+ NSDictionary* attrs = [decoded attributesAtIndex:0 effectiveRange:&range];
+ EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, kStringLength), range));
+ EXPECT_NSEQ([NSFont systemFontOfSize:26],
+ [attrs objectForKey:NSFontAttributeName]);
+ EXPECT_FALSE([attrs objectForKey:NSForegroundColorAttributeName]);
+}
+
+TEST_F(AttributedStringCoderTest, MultipleFonts) {
+ scoped_nsobject<NSMutableAttributedString> attr_str(NewAttrString());
+ [attr_str setAttributes:FontAttribute(@"Courier", 12)
+ range:NSMakeRange(0, 10)];
+ [attr_str addAttributes:FontAttribute(@"Helvetica", 16)
+ range:NSMakeRange(12, 6)];
+ [attr_str addAttributes:FontAttribute(@"Helvetica", 14)
+ range:NSMakeRange(15, 5)];
+
+ NSAttributedString* decoded = EncodeAndDecode(attr_str);
+
+ EXPECT_NSEQ(attr_str.get(), decoded);
+}
+
+TEST_F(AttributedStringCoderTest, NoPertinentAttributes) {
+ scoped_nsobject<NSMutableAttributedString> attr_str(NewAttrString());
+ [attr_str addAttribute:NSForegroundColorAttributeName
+ value:[NSColor blueColor]
+ range:NSMakeRange(0, 10)];
+ [attr_str addAttribute:NSBackgroundColorAttributeName
+ value:[NSColor blueColor]
+ range:NSMakeRange(15, 5)];
+ [attr_str addAttribute:NSKernAttributeName
+ value:[NSNumber numberWithFloat:2.6]
+ range:NSMakeRange(11, 3)];
+
+ NSAttributedString* decoded = EncodeAndDecode(attr_str.get());
+
+ scoped_nsobject<NSAttributedString> expected(NewAttrString());
+ EXPECT_NSEQ(expected.get(), decoded);
+}
+
+TEST_F(AttributedStringCoderTest, NilString) {
+ NSAttributedString* decoded = EncodeAndDecode(nil);
+ EXPECT_TRUE(decoded);
+ EXPECT_EQ(0U, [decoded length]);
+}
+
+TEST_F(AttributedStringCoderTest, OutOfRange) {
+ AttributedStringCoder::EncodedString encoded(ASCIIToUTF16("Hello World"));
+ encoded.attributes()->push_back(
+ AttributedStringCoder::FontAttribute(
+ FontDescriptor([NSFont systemFontOfSize:12]),
+ ui::Range(0, 5)));
+ encoded.attributes()->push_back(
+ AttributedStringCoder::FontAttribute(
+ FontDescriptor([NSFont systemFontOfSize:14]),
+ ui::Range(5, 100)));
+ encoded.attributes()->push_back(
+ AttributedStringCoder::FontAttribute(
+ FontDescriptor([NSFont systemFontOfSize:16]),
+ ui::Range(100, 5)));
+
+ NSAttributedString* decoded = AttributedStringCoder::Decode(&encoded);
+ EXPECT_TRUE(decoded);
+
+ NSRange range;
+ NSDictionary* attrs = [decoded attributesAtIndex:0 effectiveRange:&range];
+ EXPECT_NSEQ([NSFont systemFontOfSize:12],
+ [attrs objectForKey:NSFontAttributeName]);
+ EXPECT_TRUE(NSEqualRanges(range, NSMakeRange(0, 5)));
+
+ attrs = [decoded attributesAtIndex:5 effectiveRange:&range];
+ EXPECT_FALSE([attrs objectForKey:NSFontAttributeName]);
+ EXPECT_EQ(0U, [attrs count]);
+}
diff --git a/chrome/common/text_input_client_messages.cc b/chrome/common/text_input_client_messages.cc
new file mode 100644
index 0000000..1a85234
--- /dev/null
+++ b/chrome/common/text_input_client_messages.cc
@@ -0,0 +1,8 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/common_param_traits.h"
+
+#define IPC_MESSAGE_IMPL
+#include "chrome/common/text_input_client_messages.h"
diff --git a/chrome/common/text_input_client_messages.h b/chrome/common/text_input_client_messages.h
new file mode 100644
index 0000000..a840c2c
--- /dev/null
+++ b/chrome/common/text_input_client_messages.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_
+#define CHROME_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_
+
+#include "ipc/ipc_message_macros.h"
+#include "ui/base/range/range.h"
+#include "ui/gfx/rect.h"
+
+#if defined(OS_MACOSX)
+#include "chrome/common/attributed_string_coder_mac.h"
+#endif
+
+#define IPC_MESSAGE_START TextInputClientMsgStart
+
+// Browser -> Renderer Messages ////////////////////////////////////////////////
+// These messages are sent from the browser to the renderer. Each one has a
+// corresponding reply message.
+////////////////////////////////////////////////////////////////////////////////
+
+// Tells the renderer to send back the character index for a point.
+IPC_MESSAGE_ROUTED1(TextInputClientMsg_CharacterIndexForPoint,
+ gfx::Point)
+
+// Tells the renderer to send back the rectangle for a given character range.
+IPC_MESSAGE_ROUTED1(TextInputClientMsg_FirstRectForCharacterRange,
+ ui::Range)
+
+// Tells the renderer to send back the text fragment in a given range.
+IPC_MESSAGE_ROUTED1(TextInputClientMsg_StringForRange,
+ ui::Range)
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Renderer -> Browser Replies /////////////////////////////////////////////////
+// These messages are sent in reply to the above messages.
+////////////////////////////////////////////////////////////////////////////////
+
+// Reply message for TextInputClientMsg_CharacterIndexForPoint.
+IPC_MESSAGE_ROUTED1(TextInputClientReplyMsg_GotCharacterIndexForPoint,
+ size_t /* character index */)
+
+// Reply message for TextInputClientMsg_FirstRectForCharacterRange.
+IPC_MESSAGE_ROUTED1(TextInputClientReplyMsg_GotFirstRectForRange,
+ gfx::Rect /* frame rectangle */)
+
+#if defined(OS_MACOSX)
+// Reply message for TextInputClientMsg_StringForRange.
+IPC_MESSAGE_ROUTED1(TextInputClientReplyMsg_GotStringForRange,
+ mac::AttributedStringCoder::EncodedString)
+#endif // defined(OS_MACOSX)
+
+#endif // CHROME_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index abedf12..c786c45 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -55,6 +55,7 @@
#include "chrome/renderer/searchbox_extension.h"
#include "chrome/renderer/spellchecker/spellcheck.h"
#include "chrome/renderer/spellchecker/spellcheck_provider.h"
+#include "chrome/renderer/text_input_client_observer.h"
#include "chrome/renderer/translate_helper.h"
#include "chrome/renderer/visitedlink_slave.h"
#include "content/common/view_messages.h"
@@ -194,6 +195,10 @@ void ChromeContentRendererClient::RenderViewCreated(RenderView* render_view) {
new SpellCheckProvider(render_view, spellcheck_.get());
new safe_browsing::MalwareDOMDetails(render_view);
+#if defined(OS_MACOSX)
+ new TextInputClientObserver(render_view);
+#endif // defined(OS_MACOSX)
+
PasswordAutofillManager* password_autofill_manager =
new PasswordAutofillManager(render_view);
AutofillAgent* autofill_agent = new AutofillAgent(render_view,
diff --git a/chrome/renderer/text_input_client_observer.cc b/chrome/renderer/text_input_client_observer.cc
new file mode 100644
index 0000000..2102267
--- /dev/null
+++ b/chrome/renderer/text_input_client_observer.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/text_input_client_observer.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/common/text_input_client_messages.h"
+#include "content/renderer/render_view.h"
+#include "ipc/ipc_message_macros.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebSubstringUtil.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "ui/gfx/rect.h"
+
+TextInputClientObserver::TextInputClientObserver(RenderView* render_view)
+ : RenderViewObserver(render_view) {
+}
+
+TextInputClientObserver::~TextInputClientObserver() {
+}
+
+bool TextInputClientObserver::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(TextInputClientObserver, message)
+ IPC_MESSAGE_HANDLER(TextInputClientMsg_CharacterIndexForPoint,
+ OnCharacterIndexForPoint)
+ IPC_MESSAGE_HANDLER(TextInputClientMsg_FirstRectForCharacterRange,
+ OnFirstRectForCharacterRange)
+ IPC_MESSAGE_HANDLER(TextInputClientMsg_StringForRange, OnStringForRange)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+WebKit::WebView* TextInputClientObserver::webview() {
+ return render_view()->webview();
+}
+
+void TextInputClientObserver::OnCharacterIndexForPoint(gfx::Point point) {
+ WebKit::WebPoint web_point(point);
+ size_t index = webview()->focusedFrame()->characterIndexForPoint(web_point);
+ Send(new TextInputClientReplyMsg_GotCharacterIndexForPoint(routing_id(),
+ index));
+}
+
+void TextInputClientObserver::OnFirstRectForCharacterRange(ui::Range range) {
+ WebKit::WebFrame* frame = webview()->focusedFrame();
+ WebKit::WebRect web_rect;
+ frame->firstRectForCharacterRange(range.start(), range.length(), web_rect);
+ gfx::Rect rect(web_rect);
+ Send(new TextInputClientReplyMsg_GotFirstRectForRange(routing_id(), rect));
+}
+
+void TextInputClientObserver::OnStringForRange(ui::Range range) {
+#if defined(OS_MACOSX)
+ NSAttributedString* string =
+ WebKit::WebSubstringUtil::attributedSubstringInRange(
+ webview()->focusedFrame(), range.start(), range.length());
+ scoped_ptr<const mac::AttributedStringCoder::EncodedString> encoded(
+ mac::AttributedStringCoder::Encode(string));
+ Send(new TextInputClientReplyMsg_GotStringForRange(routing_id(),
+ *encoded.get()));
+#else
+ NOTIMPLEMENTED();
+#endif
+}
diff --git a/chrome/renderer/text_input_client_observer.h b/chrome/renderer/text_input_client_observer.h
new file mode 100644
index 0000000..bbd265a
--- /dev/null
+++ b/chrome/renderer/text_input_client_observer.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_TEXT_INPUT_CLIENT_OBSERVER_H_
+#define CHROME_RENDERER_TEXT_INPUT_CLIENT_OBSERVER_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "content/renderer/render_view_observer.h"
+#include "ui/base/range/range.h"
+#include "ui/gfx/point.h"
+
+namespace WebKit {
+class WebView;
+}
+
+// This is the renderer-side message filter that generates the replies for the
+// messages sent by the TextInputClientMac. See
+// chrome/browser/renderer_host/text_input_client_mac.h for more information.
+class TextInputClientObserver : public RenderViewObserver {
+ public:
+ explicit TextInputClientObserver(RenderView* render_view);
+ virtual ~TextInputClientObserver();
+
+ // RenderViewObserver overrides:
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ private:
+ // Returns the WebView of the RenderView.
+ WebKit::WebView* webview();
+
+ // IPC Message handlers:
+ void OnCharacterIndexForPoint(gfx::Point point);
+ void OnFirstRectForCharacterRange(ui::Range range);
+ void OnStringForRange(ui::Range range);
+
+ DISALLOW_COPY_AND_ASSIGN(TextInputClientObserver);
+};
+
+#endif // CHROME_RENDERER_TEXT_INPUT_CLIENT_OBSERVER_H_
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index 16a20be..1cc7ec9 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -558,7 +558,7 @@ void RenderMessageFilter::OnLoadFont(const FontDescriptor& font,
base::SharedMemoryHandle* handle) {
base::SharedMemory font_data;
uint32 font_data_size = 0;
- bool ok = FontLoader::LoadFontIntoBuffer(font.nsFont(), &font_data,
+ bool ok = FontLoader::LoadFontIntoBuffer(font.ToNSFont(), &font_data,
&font_data_size);
if (!ok || font_data_size == 0) {
LOG(ERROR) << "Couldn't load font data for " << font.font_name <<
diff --git a/content/browser/renderer_host/render_view_host.cc b/content/browser/renderer_host/render_view_host.cc
index 98bcbf8..da09850 100644
--- a/content/browser/renderer_host/render_view_host.cc
+++ b/content/browser/renderer_host/render_view_host.cc
@@ -1080,9 +1080,10 @@ void RenderViewHost::OnMsgSetTooltipText(
view()->SetTooltipText(UTF16ToWide(wrapped_tooltip_text));
}
-void RenderViewHost::OnMsgSelectionChanged(const std::string& text) {
+void RenderViewHost::OnMsgSelectionChanged(const std::string& text,
+ const ui::Range& range) {
if (view())
- view()->SelectionChanged(text);
+ view()->SelectionChanged(text, range);
}
void RenderViewHost::OnMsgRunJavaScriptMessage(
diff --git a/content/browser/renderer_host/render_view_host.h b/content/browser/renderer_host/render_view_host.h
index 14ecddd..2936218 100644
--- a/content/browser/renderer_host/render_view_host.h
+++ b/content/browser/renderer_host/render_view_host.h
@@ -52,6 +52,10 @@ namespace gfx {
class Point;
} // namespace gfx
+namespace ui {
+class Range;
+} // namespace ui
+
namespace webkit_glue {
struct CustomContextMenuContext;
struct WebAccessibility;
@@ -496,7 +500,7 @@ class RenderViewHost : public RenderWidgetHost {
const std::string& target);
void OnMsgSetTooltipText(const std::wstring& tooltip_text,
WebKit::WebTextDirection text_direction_hint);
- void OnMsgSelectionChanged(const std::string& text);
+ void OnMsgSelectionChanged(const std::string& text, const ui::Range& range);
void OnMsgPasteFromSelectionClipboard();
void OnMsgRunJavaScriptMessage(const std::wstring& message,
const std::wstring& default_prompt,
diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc
index 77db54b..339b310 100644
--- a/content/browser/renderer_host/render_widget_host.cc
+++ b/content/browser/renderer_host/render_widget_host.cc
@@ -174,6 +174,8 @@ bool RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnMsgSetCursor)
IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateTextInputState,
OnMsgImeUpdateTextInputState)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCompositionRangeChanged,
+ OnMsgImeCompositionRangeChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCancelComposition,
OnMsgImeCancelComposition)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidActivateAcceleratedCompositing,
@@ -1029,6 +1031,11 @@ void RenderWidgetHost::OnMsgImeUpdateTextInputState(
view_->ImeUpdateTextInputState(type, caret_rect);
}
+void RenderWidgetHost::OnMsgImeCompositionRangeChanged(const ui::Range& range) {
+ if (view_)
+ view_->ImeCompositionRangeChanged(range);
+}
+
void RenderWidgetHost::OnMsgImeCancelComposition() {
if (view_)
view_->ImeCancelComposition();
diff --git a/content/browser/renderer_host/render_widget_host.h b/content/browser/renderer_host/render_widget_host.h
index d110436..13aa909 100644
--- a/content/browser/renderer_host/render_widget_host.h
+++ b/content/browser/renderer_host/render_widget_host.h
@@ -31,6 +31,10 @@ namespace gfx {
class Rect;
}
+namespace ui {
+class Range;
+}
+
namespace WebKit {
class WebInputEvent;
class WebMouseEvent;
@@ -475,6 +479,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
void OnMsgSetCursor(const WebCursor& cursor);
void OnMsgImeUpdateTextInputState(WebKit::WebTextInputType type,
const gfx::Rect& caret_rect);
+ void OnMsgImeCompositionRangeChanged(const ui::Range& range);
void OnMsgImeCancelComposition();
void OnMsgDidActivateAcceleratedCompositing(bool activated);
diff --git a/content/browser/renderer_host/render_widget_host_view.h b/content/browser/renderer_host/render_widget_host_view.h
index 76c1db4..f443d1c 100644
--- a/content/browser/renderer_host/render_widget_host_view.h
+++ b/content/browser/renderer_host/render_widget_host_view.h
@@ -18,6 +18,7 @@
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextInputType.h"
+#include "ui/base/range/range.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/surface/transport_dib.h"
@@ -139,6 +140,9 @@ class RenderWidgetHostView {
// Cancel the ongoing composition of the input method attached to the view.
virtual void ImeCancelComposition() = 0;
+ // Updates the range of the marked text in an IME composition.
+ virtual void ImeCompositionRangeChanged(const ui::Range& range) {}
+
// Informs the view that a portion of the widget's backing store was scrolled
// and/or painted. The view should ensure this gets copied to the screen.
//
@@ -176,7 +180,8 @@ class RenderWidgetHostView {
virtual void SetTooltipText(const std::wstring& tooltip_text) = 0;
// Notifies the View that the renderer text selection has changed.
- virtual void SelectionChanged(const std::string& text) {}
+ virtual void SelectionChanged(const std::string& text,
+ const ui::Range& range) {}
// Tells the View whether the context menu is showing. This is used on Linux
// to suppress updates to webkit focus for the duration of the show.
diff --git a/content/common/common_param_traits.cc b/content/common/common_param_traits.cc
index 1e0ffd0..5e31003 100644
--- a/content/common/common_param_traits.cc
+++ b/content/common/common_param_traits.cc
@@ -9,6 +9,7 @@
#include "net/base/upload_data.h"
#include "net/http/http_response_headers.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/range/range.h"
#include "ui/gfx/rect.h"
namespace {
@@ -501,6 +502,24 @@ void ParamTraits<gfx::Rect>::Log(const gfx::Rect& p, std::string* l) {
p.width(), p.height()));
}
+void ParamTraits<ui::Range>::Write(Message* m, const ui::Range& r) {
+ m->WriteSize(r.start());
+ m->WriteSize(r.end());
+}
+
+bool ParamTraits<ui::Range>::Read(const Message* m, void** iter, ui::Range* r) {
+ size_t start, end;
+ if (!m->ReadSize(iter, &start) || !m->ReadSize(iter, &end))
+ return false;
+ r->set_start(start);
+ r->set_end(end);
+ return true;
+}
+
+void ParamTraits<ui::Range>::Log(const ui::Range& r, std::string* l) {
+ l->append(base::StringPrintf("(%"PRIuS", %"PRIuS")", r.start(), r.end()));
+}
+
void ParamTraits<SkBitmap>::Write(Message* m, const SkBitmap& p) {
size_t fixed_size = sizeof(SkBitmap_Data);
SkBitmap_Data bmp_data;
diff --git a/content/common/common_param_traits.h b/content/common/common_param_traits.h
index ae102cb..41e0326 100644
--- a/content/common/common_param_traits.h
+++ b/content/common/common_param_traits.h
@@ -38,6 +38,10 @@ class HostPortPair;
class UploadData;
}
+namespace ui {
+class Range;
+}
+
namespace IPC {
template <>
@@ -165,6 +169,14 @@ struct ParamTraits<gfx::NativeWindow> {
}
};
+template <>
+struct ParamTraits<ui::Range> {
+ typedef ui::Range param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::string* l);
+};
+
#if defined(OS_WIN)
template<>
struct ParamTraits<TransportDIB::Id> {
diff --git a/content/common/font_descriptor_mac.h b/content/common/font_descriptor_mac.h
index 91bb338..c3ac5a8 100644
--- a/content/common/font_descriptor_mac.h
+++ b/content/common/font_descriptor_mac.h
@@ -17,11 +17,12 @@ class NSFont;
// Container to allow serializing an NSFont over IPC.
struct FontDescriptor {
explicit FontDescriptor(NSFont* font);
+ FontDescriptor(string16 name, float size);
FontDescriptor() : font_point_size(0) {}
// Return an autoreleased NSFont corresponding to the font description.
- NSFont* nsFont() const;
+ NSFont* ToNSFont() const;
// Name of the font.
string16 font_name;
diff --git a/content/common/font_descriptor_mac.mm b/content/common/font_descriptor_mac.mm
index 90c7986..c8e3112 100644
--- a/content/common/font_descriptor_mac.mm
+++ b/content/common/font_descriptor_mac.mm
@@ -13,7 +13,12 @@ FontDescriptor::FontDescriptor(NSFont* font) {
font_point_size = [font pointSize];
}
-NSFont* FontDescriptor::nsFont() const {
+FontDescriptor::FontDescriptor(string16 name, float size) {
+ font_name = name;
+ font_point_size = size;
+}
+
+NSFont* FontDescriptor::ToNSFont() const {
NSString* font_name_ns = base::SysUTF16ToNSString(font_name);
NSFont* font = [NSFont fontWithName:font_name_ns size:font_point_size];
return font;
diff --git a/content/common/font_descriptor_mac_unittest.mm b/content/common/font_descriptor_mac_unittest.mm
index d4a74d1..f8fdfc8 100644
--- a/content/common/font_descriptor_mac_unittest.mm
+++ b/content/common/font_descriptor_mac_unittest.mm
@@ -72,7 +72,7 @@ NSFont* MakeNSFont(const std::string& font_name, float font_point_size) {
FontDescriptor desc;
desc.font_name = UTF8ToUTF16(font_name);
desc.font_point_size = font_point_size;
- return desc.nsFont();
+ return desc.ToNSFont();
}
// Verify that serialization and deserialization of fonts with various styles
@@ -81,12 +81,12 @@ TEST_F(FontSerializationTest, StyledFonts) {
NSFont* plain_font = [NSFont systemFontOfSize:12.0];
ASSERT_TRUE(plain_font != nil);
FontDescriptor desc_plain(plain_font);
- EXPECT_TRUE(CompareFonts(plain_font, desc_plain.nsFont()));
+ EXPECT_TRUE(CompareFonts(plain_font, desc_plain.ToNSFont()));
NSFont* bold_font = [NSFont boldSystemFontOfSize:30.0];
ASSERT_TRUE(bold_font != nil);
FontDescriptor desc_bold(bold_font);
- EXPECT_TRUE(CompareFonts(bold_font, desc_bold.nsFont()));
+ EXPECT_TRUE(CompareFonts(bold_font, desc_bold.ToNSFont()));
NSFont* italic_bold_font =
[[NSFontManager sharedFontManager]
@@ -96,7 +96,7 @@ TEST_F(FontSerializationTest, StyledFonts) {
size:18.0];
ASSERT_TRUE(italic_bold_font != nil);
FontDescriptor desc_italic_bold(italic_bold_font);
- EXPECT_TRUE(CompareFonts(italic_bold_font, desc_italic_bold.nsFont()));
+ EXPECT_TRUE(CompareFonts(italic_bold_font, desc_italic_bold.ToNSFont()));
}
// Test that FontDescriptor doesn't crash when used with bad parameters.
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 55a118c..ea324b7 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -25,6 +25,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextInputType.h"
+#include "ui/base/range/range.h"
#include "ui/gfx/rect.h"
#include "webkit/glue/context_menu.h"
#include "webkit/glue/password_form.h"
@@ -1676,8 +1677,9 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_SetTooltipText,
WebKit::WebTextDirection /* text direction hint */)
// Notification that the text selection has changed.
-IPC_MESSAGE_ROUTED1(ViewHostMsg_SelectionChanged,
- std::string /* currently selected text */)
+IPC_MESSAGE_ROUTED2(ViewHostMsg_SelectionChanged,
+ std::string /* currently selected text */,
+ ui::Range /* selection range */)
// Asks the browser to display the file chooser. The result is returned in a
// ViewHost_RunFileChooserResponse message.
@@ -1709,6 +1711,11 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_ImeUpdateTextInputState,
WebKit::WebTextInputType, /* text_input_type */
gfx::Rect /* caret_rect */)
+
+// Message sent when the IME text composition range changes.
+IPC_MESSAGE_ROUTED1(ViewHostMsg_ImeCompositionRangeChanged,
+ ui::Range /* composition range */)
+
// Required for cancelling an ongoing input method composition.
IPC_MESSAGE_ROUTED0(ViewHostMsg_ImeCancelComposition)
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 998641c..24ef258 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -15,6 +15,7 @@
'../third_party/npapi/npapi.gyp:npapi',
'../third_party/WebKit/Source/WebKit/chromium/WebKit.gyp:webkit',
'../ui/gfx/gl/gl.gyp:gl',
+ '../ui/ui.gyp:ui_base',
'../webkit/support/webkit_support.gyp:appcache',
'../webkit/support/webkit_support.gyp:blob',
'../webkit/support/webkit_support.gyp:database',
diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc
index 19bbaac..eafbd1d 100644
--- a/content/renderer/render_view.cc
+++ b/content/renderer/render_view.cc
@@ -1423,25 +1423,27 @@ void RenderView::didChangeSelection(bool is_empty_selection) {
#if defined(OS_POSIX)
if (!handling_input_event_)
return;
- // TODO(estade): investigate incremental updates to the selection so that we
- // don't send the entire selection over IPC every time.
- if (!is_empty_selection) {
+
+ if (is_empty_selection) {
+ last_selection_.clear();
+ } else {
// Sometimes we get repeated didChangeSelection calls from webkit when
// the selection hasn't actually changed. We don't want to report these
// because it will cause us to continually claim the X clipboard.
- const std::string& this_selection =
- webview()->focusedFrame()->selectionAsText().utf8();
+ WebFrame* frame = webview()->focusedFrame();
+ const std::string& this_selection = frame->selectionAsText().utf8();
if (this_selection == last_selection_)
return;
-
- Send(new ViewHostMsg_SelectionChanged(routing_id_,
- this_selection));
last_selection_ = this_selection;
- } else {
- last_selection_.clear();
- Send(new ViewHostMsg_SelectionChanged(routing_id_,
- last_selection_));
}
+
+ ui::Range range(ui::Range::InvalidRange());
+ size_t location, length;
+ if (webview()->caretOrSelectionRange(&location, &length)) {
+ range.set_start(location);
+ range.set_end(location + length);
+ }
+ Send(new ViewHostMsg_SelectionChanged(routing_id_, last_selection_, range));
#endif // defined(OS_POSIX)
}
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 26f3f99..6b190b3 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -22,6 +22,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenuInfo.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebRange.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h"
@@ -48,6 +49,7 @@ using WebKit::WebNavigationPolicy;
using WebKit::WebPopupMenu;
using WebKit::WebPopupMenuInfo;
using WebKit::WebPopupType;
+using WebKit::WebRange;
using WebKit::WebRect;
using WebKit::WebScreenInfo;
using WebKit::WebSize;
@@ -886,19 +888,51 @@ void RenderWidget::OnImeSetComposition(
int selection_start, int selection_end) {
if (!webwidget_)
return;
- if (!webwidget_->setComposition(
+ if (webwidget_->setComposition(
text, WebVector<WebCompositionUnderline>(underlines),
selection_start, selection_end)) {
+ // Setting the IME composition was successful. Send the new composition
+ // range to the browser.
+ ui::Range range(ui::Range::InvalidRange());
+ size_t location, length;
+ if (webwidget_->compositionRange(&location, &length)) {
+ range.set_start(location);
+ range.set_end(location + length);
+ }
+ // The IME was cancelled via the Esc key, so just send back the caret.
+ else if (webwidget_->caretOrSelectionRange(&location, &length)) {
+ range.set_start(location);
+ range.set_end(location + length);
+ }
+ Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range));
+ } else {
// If we failed to set the composition text, then we need to let the browser
// process to cancel the input method's ongoing composition session, to make
// sure we are in a consistent state.
Send(new ViewHostMsg_ImeCancelComposition(routing_id()));
+
+ // Send an updated IME range with just the caret range.
+ ui::Range range(ui::Range::InvalidRange());
+ size_t location, length;
+ if (webwidget_->caretOrSelectionRange(&location, &length)) {
+ range.set_start(location);
+ range.set_end(location + length);
+ }
+ Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range));
}
}
void RenderWidget::OnImeConfirmComposition(const string16& text) {
if (webwidget_)
webwidget_->confirmComposition(text);
+ // Send an updated IME range with just the caret range.
+ ui::Range range(ui::Range::InvalidRange());
+ size_t location, length;
+ if (webwidget_->caretOrSelectionRange(&location, &length)) {
+ range.set_start(location);
+ range.set_end(location + length);
+ }
+ Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range));
}
// This message causes the renderer to render an image of the
@@ -1090,6 +1124,15 @@ void RenderWidget::resetInputMethod() {
if (webwidget_->confirmComposition())
Send(new ViewHostMsg_ImeCancelComposition(routing_id()));
}
+
+ // Send an updated IME range with the current caret rect.
+ ui::Range range(ui::Range::InvalidRange());
+ size_t location, length;
+ if (webwidget_->caretOrSelectionRange(&location, &length)) {
+ range.set_start(location);
+ range.set_end(location + length);
+ }
+ Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range));
}
void RenderWidget::SchedulePluginMove(
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index d2ab734..ae8aeb7 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -161,10 +161,6 @@ class PepperWidget : public WebWidget {
return false;
}
- virtual bool selectionRange(size_t *location, size_t *length) {
- return false;
- }
-
virtual bool caretOrSelectionRange(size_t* location, size_t* length) {
return false;
}
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index c353e95..1aff129 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -89,6 +89,7 @@ enum IPCMessageStart {
VideoCaptureMsgStart,
QuotaMsgStart,
IconMsgStart,
+ TextInputClientMsgStart,
LastIPCMsgStart // Must come last.
};