Standardize IME callbacks for off-screen rendering (issue #1675)

This commit is contained in:
Marshall Greenblatt 2016-10-28 12:11:24 -04:00
parent e69de63b15
commit d6b17a8fb5
48 changed files with 1999 additions and 976 deletions

View File

@ -30,3 +30,4 @@ YuTeh Shen <shenyute@gmail.com>
Andrei Kurushin <ajax16384@gmail.com>
Gonzo Berman <gberman@factset.com>
Jakub Trzebiatowski <kuba.trzebiatowski@gmail.com>
Nishant Kaushik <nishantk@adobe.com>

View File

@ -710,8 +710,6 @@ static_library("libcef_static") {
"libcef/browser/osr/browser_platform_delegate_osr_mac.h",
"libcef/browser/osr/browser_platform_delegate_osr_mac.mm",
"libcef/browser/osr/render_widget_host_view_osr_mac.mm",
"libcef/browser/osr/text_input_client_osr_mac.mm",
"libcef/browser/osr/text_input_client_osr_mac.h",
"libcef/common/util_mac.h",
"libcef/common/util_mac.mm",
]
@ -1794,6 +1792,7 @@ if (is_mac) {
"rpcrt4.lib",
"opengl32.lib",
"glu32.lib",
"imm32.lib",
]
}

View File

@ -248,6 +248,8 @@
'tests/cefclient/browser/main_message_loop_multithreaded_win.h',
'tests/cefclient/browser/osr_dragdrop_win.cc',
'tests/cefclient/browser/osr_dragdrop_win.h',
'tests/cefclient/browser/osr_ime_handler_win.cc',
'tests/cefclient/browser/osr_ime_handler_win.h',
'tests/cefclient/browser/osr_window_win.cc',
'tests/cefclient/browser/osr_window_win.h',
'tests/cefclient/browser/resource_util_win.cc',
@ -284,6 +286,8 @@
'tests/cefclient/browser/root_window_mac.mm',
'tests/cefclient/browser/temp_window_mac.h',
'tests/cefclient/browser/temp_window_mac.mm',
'tests/cefclient/browser/text_input_client_osr_mac.h',
'tests/cefclient/browser/text_input_client_osr_mac.mm',
'tests/cefclient/browser/window_test_runner_mac.h',
'tests/cefclient/browser/window_test_runner_mac.mm',
'tests/cefclient/cefclient_mac.mm',

View File

@ -618,24 +618,62 @@ typedef struct _cef_browser_host_t {
struct _cef_browser_host_t* self, int frame_rate);
///
// Get the NSTextInputContext implementation for enabling IME on Mac when
// window rendering is disabled.
// Begins a new composition or updates the existing composition. Blink has a
// special node (a composition node) that allows the input function to change
// text without affecting other DOM nodes. |text| is the optional text that
// will be inserted into the composition node. |underlines| is an optional set
// of ranges that will be underlined in the resulting text.
// |replacement_range| is an optional range of the existing text that will be
// replaced. |selection_range| is an optional range of the resulting text that
// will be selected after insertion or replacement. The |replacement_range|
// value is only used on OS X.
//
// This function may be called multiple times as the composition changes. When
// the client is done making changes the composition should either be canceled
// or completed. To cancel the composition call ImeCancelComposition. To
// complete the composition call either ImeCommitText or
// ImeFinishComposingText. Completion is usually signaled when:
// A. The client receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR
// flag (on Windows), or;
// B. The client receives a "commit" signal of GtkIMContext (on Linux), or;
// C. insertText of NSTextInput is called (on Mac).
//
// This function is only used when window rendering is disabled.
///
cef_text_input_context_t (CEF_CALLBACK *get_nstext_input_context)(
struct _cef_browser_host_t* self);
void (CEF_CALLBACK *ime_set_composition)(struct _cef_browser_host_t* self,
const cef_string_t* text, size_t underlinesCount,
cef_composition_underline_t const* underlines,
const cef_range_t* replacement_range,
const cef_range_t* selection_range);
///
// Handles a keyDown event prior to passing it through the NSTextInputClient
// machinery.
// Completes the existing composition by optionally inserting the specified
// |text| into the composition node. |replacement_range| is an optional range
// of the existing text that will be replaced. |relative_cursor_pos| is where
// the cursor will be positioned relative to the current cursor position. See
// comments on ImeSetComposition for usage. The |replacement_range| and
// |relative_cursor_pos| values are only used on OS X. This function is only
// used when window rendering is disabled.
///
void (CEF_CALLBACK *handle_key_event_before_text_input_client)(
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent);
void (CEF_CALLBACK *ime_commit_text)(struct _cef_browser_host_t* self,
const cef_string_t* text, const cef_range_t* replacement_range,
int relative_cursor_pos);
///
// Performs any additional actions after NSTextInputClient handles the event.
// Completes the existing composition by applying the current composition node
// contents. If |keep_selection| is false (0) the current selection, if any,
// will be discarded. See comments on ImeSetComposition for usage. This
// function is only used when window rendering is disabled.
///
void (CEF_CALLBACK *handle_key_event_after_text_input_client)(
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent);
void (CEF_CALLBACK *ime_finish_composing_text)(
struct _cef_browser_host_t* self, int keep_selection);
///
// Cancels the existing composition and discards the composition node contents
// without applying them. See comments on ImeSetComposition for usage. This
// function is only used when window rendering is disabled.
///
void (CEF_CALLBACK *ime_cancel_composition)(struct _cef_browser_host_t* self);
///
// Call this function when the user drags the mouse into the web view (before

View File

@ -161,6 +161,16 @@ typedef struct _cef_render_handler_t {
void (CEF_CALLBACK *on_scroll_offset_changed)(
struct _cef_render_handler_t* self, struct _cef_browser_t* browser,
double x, double y);
///
// Called when the IME composition range has changed. |selected_range| is the
// range of characters that have been selected. |character_bounds| is the
// bounds of each character in view coordinates.
///
void (CEF_CALLBACK *on_ime_composition_range_changed)(
struct _cef_render_handler_t* self, struct _cef_browser_t* browser,
const cef_range_t* selected_range, size_t character_boundsCount,
cef_rect_t const* character_bounds);
} cef_render_handler_t;

View File

@ -669,24 +669,66 @@ class CefBrowserHost : public virtual CefBase {
virtual void SetWindowlessFrameRate(int frame_rate) =0;
///
// Get the NSTextInputContext implementation for enabling IME on Mac when
// window rendering is disabled.
// Begins a new composition or updates the existing composition. Blink has a
// special node (a composition node) that allows the input method to change
// text without affecting other DOM nodes. |text| is the optional text that
// will be inserted into the composition node. |underlines| is an optional set
// of ranges that will be underlined in the resulting text.
// |replacement_range| is an optional range of the existing text that will be
// replaced. |selection_range| is an optional range of the resulting text that
// will be selected after insertion or replacement. The |replacement_range|
// value is only used on OS X.
//
// This method may be called multiple times as the composition changes. When
// the client is done making changes the composition should either be canceled
// or completed. To cancel the composition call ImeCancelComposition. To
// complete the composition call either ImeCommitText or
// ImeFinishComposingText. Completion is usually signaled when:
// A. The client receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR
// flag (on Windows), or;
// B. The client receives a "commit" signal of GtkIMContext (on Linux), or;
// C. insertText of NSTextInput is called (on Mac).
//
// This method is only used when window rendering is disabled.
///
/*--cef(default_retval=NULL)--*/
virtual CefTextInputContext GetNSTextInputContext() =0;
/*--cef(optional_param=text, optional_param=underlines)--*/
virtual void ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) =0;
///
// Handles a keyDown event prior to passing it through the NSTextInputClient
// machinery.
// Completes the existing composition by optionally inserting the specified
// |text| into the composition node. |replacement_range| is an optional range
// of the existing text that will be replaced. |relative_cursor_pos| is where
// the cursor will be positioned relative to the current cursor position. See
// comments on ImeSetComposition for usage. The |replacement_range| and
// |relative_cursor_pos| values are only used on OS X.
// This method is only used when window rendering is disabled.
///
/*--cef()--*/
virtual void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) =0;
/*--cef(optional_param=text)--*/
virtual void ImeCommitText(const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos) =0;
///
// Performs any additional actions after NSTextInputClient handles the event.
// Completes the existing composition by applying the current composition node
// contents. If |keep_selection| is false the current selection, if any, will
// be discarded. See comments on ImeSetComposition for usage.
// This method is only used when window rendering is disabled.
///
/*--cef()--*/
virtual void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) =0;
virtual void ImeFinishComposingText(bool keep_selection) =0;
///
// Cancels the existing composition and discards the composition node
// contents without applying them. See comments on ImeSetComposition for
// usage.
// This method is only used when window rendering is disabled.
///
/*--cef()--*/
virtual void ImeCancelComposition() =0;
///
// Call this method when the user drags the mouse into the web view (before

View File

@ -176,6 +176,16 @@ class CefRenderHandler : public virtual CefBase {
virtual void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser,
double x,
double y) {}
///
// Called when the IME composition range has changed. |selected_range| is the
// range of characters that have been selected. |character_bounds| is the
// bounds of each character in view coordinates.
///
/*--cef()--*/
virtual void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
const CefRange& selected_range,
const RectList& character_bounds) {}
};
#endif // CEF_INCLUDE_CEF_RENDER_HANDLER_H_

View File

@ -39,7 +39,6 @@
#define CefCursorHandle cef_cursor_handle_t
#define CefEventHandle cef_event_handle_t
#define CefWindowHandle cef_window_handle_t
#define CefTextInputContext cef_text_input_context_t
struct CefMainArgsTraits {
typedef cef_main_args_t struct_type;

View File

@ -39,7 +39,6 @@
#define CefCursorHandle cef_cursor_handle_t
#define CefEventHandle cef_event_handle_t
#define CefWindowHandle cef_window_handle_t
#define CefTextInputContext cef_text_input_context_t
struct CefMainArgsTraits {
typedef cef_main_args_t struct_type;

View File

@ -2682,6 +2682,33 @@ typedef enum {
CEF_CDM_REGISTRATION_ERROR_NOT_SUPPORTED,
} cef_cdm_registration_error_t;
///
// Structure representing IME composition underline information. This is a thin
// wrapper around Blink's WebCompositionUnderline class and should be kept in
// sync with that.
///
typedef struct _cef_composition_underline_t {
///
// Underline character range.
///
cef_range_t range;
///
// Text color.
///
cef_color_t color;
///
// Background color.
///
cef_color_t background_color;
///
// Set to true (1) for thick underline.
///
int thick;
} cef_composition_underline_t;
#ifdef __cplusplus
}
#endif

View File

@ -60,7 +60,6 @@ extern "C" {
// thread-safe and must only be accessed on the browser process UI thread.
///
CEF_EXPORT XDisplay* cef_get_xdisplay();
#define cef_text_input_context_t void*
///
// Structure representing CefExecuteProcess arguments.

View File

@ -43,22 +43,18 @@
@class NSCursor;
@class NSEvent;
@class NSView;
@class NSTextInputContext;
#else
class NSCursor;
class NSEvent;
struct NSView;
class NSTextInputContext;
#endif
#define cef_cursor_handle_t NSCursor*
#define cef_event_handle_t NSEvent*
#define cef_window_handle_t NSView*
#define cef_text_input_context_t NSTextInputContext*
#else
#define cef_cursor_handle_t void*
#define cef_event_handle_t void*
#define cef_window_handle_t void*
#define cef_text_input_context_t void*
#endif
#define kNullCursorHandle NULL

View File

@ -42,7 +42,6 @@
#define cef_cursor_handle_t HCURSOR
#define cef_event_handle_t MSG*
#define cef_window_handle_t HWND
#define cef_text_input_context_t void*
#define kNullCursorHandle NULL
#define kNullEventHandle NULL

View File

@ -975,4 +975,31 @@ struct CefBoxLayoutSettingsTraits {
///
typedef CefStructBase<CefBoxLayoutSettingsTraits> CefBoxLayoutSettings;
struct CefCompositionUnderlineTraits {
typedef cef_composition_underline_t struct_type;
static inline void init(struct_type* s) {
s->range = {0, 0};
s->color = 0;
s->background_color = 0;
s->thick = 0;
}
static inline void clear(struct_type* s) {
}
static inline void set(const struct_type* src, struct_type* target,
bool copy) {
target->range = src->range;
target->color = src->color;
target->background_color = src->background_color;
target->thick = src->thick;
}
};
///
// Class representing IME composition underline.
///
typedef CefStructBase<CefCompositionUnderlineTraits> CefCompositionUnderline;
#endif // CEF_INCLUDE_INTERNAL_CEF_TYPES_WRAPPERS_H_

View File

@ -41,7 +41,6 @@
#define CefCursorHandle cef_cursor_handle_t
#define CefEventHandle cef_event_handle_t
#define CefWindowHandle cef_window_handle_t
#define CefTextInputContext cef_text_input_context_t
struct CefMainArgsTraits {
typedef cef_main_args_t struct_type;

View File

@ -1797,65 +1797,87 @@ void CefBrowserHostImpl::FindReply(
}
}
CefTextInputContext CefBrowserHostImpl::GetNSTextInputContext() {
#if defined(OS_MACOSX)
void CefBrowserHostImpl::ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) {
if (!IsWindowless()) {
NOTREACHED() << "Window rendering is not disabled";
return nullptr;
return;
}
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "Called on invalid thread";
return nullptr;
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefBrowserHostImpl::ImeSetComposition, this, text,
underlines, replacement_range, selection_range));
return;
}
if (!web_contents() || !platform_delegate_)
return nullptr;
return;
return platform_delegate_->GetNSTextInputContext();
#else
return nullptr;
#endif
platform_delegate_->ImeSetComposition(text, underlines, replacement_range,
selection_range);
}
void CefBrowserHostImpl::HandleKeyEventBeforeTextInputClient(
CefEventHandle keyEvent) {
#if defined(OS_MACOSX)
void CefBrowserHostImpl::ImeCommitText(const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos) {
if (!IsWindowless()) {
NOTREACHED() << "Window rendering is not disabled";
return;
}
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "Called on invalid thread";
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefBrowserHostImpl::ImeCommitText, this, text,
replacement_range, relative_cursor_pos));
return;
}
if (!web_contents() || !platform_delegate_)
return;
platform_delegate_->HandleKeyEventBeforeTextInputClient(keyEvent);
#endif
platform_delegate_->ImeCommitText(text, replacement_range,
relative_cursor_pos);
}
void CefBrowserHostImpl::HandleKeyEventAfterTextInputClient(
CefEventHandle keyEvent) {
#if defined(OS_MACOSX)
void CefBrowserHostImpl::ImeFinishComposingText(bool keep_selection) {
if (!IsWindowless()) {
NOTREACHED() << "Window rendering is not disabled";
return;
}
if (!CEF_CURRENTLY_ON_UIT()) {
NOTREACHED() << "Called on invalid thread";
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefBrowserHostImpl::ImeFinishComposingText, this,
keep_selection));
return;
}
if (!web_contents() || !platform_delegate_)
return;
return platform_delegate_->HandleKeyEventAfterTextInputClient(keyEvent);
#endif
platform_delegate_->ImeFinishComposingText(keep_selection);
}
void CefBrowserHostImpl::ImeCancelComposition() {
if (!IsWindowless()) {
NOTREACHED() << "Window rendering is not disabled";
return;
}
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefBrowserHostImpl::ImeCancelComposition, this));
return;
}
if (!web_contents() || !platform_delegate_)
return;
platform_delegate_->ImeCancelComposition();
}
void CefBrowserHostImpl::DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,

View File

@ -206,9 +206,14 @@ class CefBrowserHostImpl : public CefBrowserHost,
void NotifyMoveOrResizeStarted() override;
int GetWindowlessFrameRate() override;
void SetWindowlessFrameRate(int frame_rate) override;
CefTextInputContext GetNSTextInputContext() override;
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) override;
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) override;
void ImeSetComposition(const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) override;
void ImeCommitText(const CefString& text, const CefRange& replacement_range,
int relative_cursor_pos) override;
void ImeFinishComposingText(bool keep_selection) override;
void ImeCancelComposition() override;
void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
const CefMouseEvent& event,
DragOperationsMask allowed_ops) override;

View File

@ -134,22 +134,27 @@ void CefBrowserPlatformDelegate::SetWindowlessFrameRate(int frame_rate) {
NOTREACHED();
}
#if defined(OS_MACOSX)
CefTextInputContext CefBrowserPlatformDelegate::GetNSTextInputContext() {
NOTREACHED();
return nullptr;
}
void CefBrowserPlatformDelegate::HandleKeyEventBeforeTextInputClient(
CefEventHandle keyEvent) {
void CefBrowserPlatformDelegate::ImeSetComposition(const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) {
NOTREACHED();
}
void CefBrowserPlatformDelegate::HandleKeyEventAfterTextInputClient(
CefEventHandle keyEvent) {
void CefBrowserPlatformDelegate::ImeCommitText(
const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos) {
NOTREACHED();
}
void CefBrowserPlatformDelegate::ImeFinishComposingText(bool keep_selection) {
NOTREACHED();
}
void CefBrowserPlatformDelegate::ImeCancelComposition() {
NOTREACHED();
}
#endif
void CefBrowserPlatformDelegate::DragTargetDragEnter(
CefRefPtr<CefDragData> drag_data,

View File

@ -222,13 +222,18 @@ class CefBrowserPlatformDelegate {
// Set the windowless frame rate. Only used with windowless rendering.
virtual void SetWindowlessFrameRate(int frame_rate);
#if defined(OS_MACOSX)
// IME-related callbacks. See documentation in CefRenderHandler. Only used
// with windowless rendering on OS X.
virtual CefTextInputContext GetNSTextInputContext();
virtual void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent);
virtual void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent);
#endif
// IME-related callbacks. See documentation in CefBrowser and
// CefRenderHandler. Only used with windowless rendering.
virtual void ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range);
virtual void ImeCommitText(const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos);
virtual void ImeFinishComposingText(bool keep_selection);
virtual void ImeCancelComposition();
// Drag/drop-related callbacks. See documentation in CefRenderHandler. Only
// used with windowless rendering.

View File

@ -209,6 +209,40 @@ void CefBrowserPlatformDelegateOsr::SetWindowlessFrameRate(int frame_rate) {
view->UpdateFrameRate();
}
void CefBrowserPlatformDelegateOsr::ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view) {
view->ImeSetComposition(text, underlines,
replacement_range, selection_range);
}
}
void CefBrowserPlatformDelegateOsr::ImeCommitText(
const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
view->ImeCommitText(text, replacement_range, relative_cursor_pos);
}
void CefBrowserPlatformDelegateOsr::ImeFinishComposingText(
bool keep_selection) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
view->ImeFinishComposingText(keep_selection);
}
void CefBrowserPlatformDelegateOsr::ImeCancelComposition() {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
view->ImeCancelComposition();
}
void CefBrowserPlatformDelegateOsr::DragTargetDragEnter(
CefRefPtr<CefDragData> drag_data,
const CefMouseEvent& event,

View File

@ -56,6 +56,15 @@ class CefBrowserPlatformDelegateOsr :
void NotifyScreenInfoChanged() override;
void Invalidate(cef_paint_element_type_t type) override;
void SetWindowlessFrameRate(int frame_rate) override;
void ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) override;
void ImeCommitText(const CefString& text, const CefRange& replacement_range,
int relative_cursor_pos) override;
void ImeFinishComposingText(bool keep_selection) override;
void ImeCancelComposition() override;
void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
const CefMouseEvent& event,
cef_drag_operations_mask_t allowed_ops) override;

View File

@ -15,9 +15,6 @@ class CefBrowserPlatformDelegateOsrMac : public CefBrowserPlatformDelegateOsr {
// CefBrowserPlatformDelegate methods:
CefWindowHandle GetHostWindowHandle() const override;
CefTextInputContext GetNSTextInputContext() override;
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) override;
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) override;
};
#endif // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_OSR_MAC_H_

View File

@ -18,23 +18,3 @@ CefWindowHandle CefBrowserPlatformDelegateOsrMac::GetHostWindowHandle() const {
return native_delegate_->window_info().parent_view;
}
CefTextInputContext CefBrowserPlatformDelegateOsrMac::GetNSTextInputContext() {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
return view->GetNSTextInputContext();
return nullptr;
}
void CefBrowserPlatformDelegateOsrMac::HandleKeyEventBeforeTextInputClient(
CefEventHandle keyEvent) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
view->HandleKeyEventBeforeTextInputClient(keyEvent);
}
void CefBrowserPlatformDelegateOsrMac::HandleKeyEventAfterTextInputClient(
CefEventHandle keyEvent) {
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
if (view)
view->HandleKeyEventAfterTextInputClient(keyEvent);
}

View File

@ -28,6 +28,7 @@
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/resize_lock.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/context_factory.h"
@ -468,9 +469,6 @@ CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR(
is_showing_(!render_widget_host_->is_hidden()),
is_destroyed_(false),
is_scroll_offset_changed_pending_(false),
#if defined(OS_MACOSX)
text_input_context_osr_mac_(NULL),
#endif
weak_ptr_factory_(this) {
DCHECK(render_widget_host_);
DCHECK(!render_widget_host_->GetView());
@ -580,10 +578,6 @@ gfx::NativeViewAccessible
return gfx::NativeViewAccessible();
}
ui::TextInputClient* CefRenderWidgetHostViewOSR::GetTextInputClient() {
return NULL;
}
void CefRenderWidgetHostViewOSR::Focus() {
}
@ -828,15 +822,6 @@ void CefRenderWidgetHostViewOSR::UpdateCursor(
void CefRenderWidgetHostViewOSR::SetIsLoading(bool is_loading) {
}
#if !defined(OS_MACOSX)
void CefRenderWidgetHostViewOSR::TextInputStateChanged(
const content::TextInputState& params) {
}
void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
}
#endif // !defined(OS_MACOSX)
void CefRenderWidgetHostViewOSR::RenderProcessGone(
base::TerminationStatus status,
int error_code) {
@ -884,12 +869,6 @@ gfx::Size CefRenderWidgetHostViewOSR::GetPhysicalBackingSize() const {
return gfx::ConvertSizeToPixel(scale_factor_, GetRequestedRendererSize());
}
#if !defined(OS_MACOSX)
void CefRenderWidgetHostViewOSR::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) {
}
#endif
void CefRenderWidgetHostViewOSR::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
@ -963,12 +942,71 @@ void CefRenderWidgetHostViewOSR::ShowDisambiguationPopup(
}
#endif
#if !defined(OS_MACOSX) && defined(USE_AURA)
void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) {
void CefRenderWidgetHostViewOSR::ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) {
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeSetComposition");
if (!render_widget_host_)
return;
std::vector<blink::WebCompositionUnderline> web_underlines;
web_underlines.reserve(underlines.size());
for (const CefCompositionUnderline& line : underlines) {
web_underlines.push_back(
blink::WebCompositionUnderline(line.range.from,
line.range.to,
line.color,
line.thick ? true : false,
line.background_color));
}
gfx::Range range(replacement_range.from, replacement_range.to);
// Start Monitoring for composition updates before we set.
RequestImeCompositionUpdate(true);
render_widget_host_->ImeSetComposition(text, web_underlines, range,
selection_range.from,
selection_range.to);
}
void CefRenderWidgetHostViewOSR::ImeCommitText(
const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos) {
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeCommitText");
if (!render_widget_host_)
return;
gfx::Range range(replacement_range.from, replacement_range.to);
render_widget_host_->ImeCommitText(text, range, relative_cursor_pos);
// Stop Monitoring for composition updates after we are done.
RequestImeCompositionUpdate(false);
}
void CefRenderWidgetHostViewOSR::ImeFinishComposingText(bool keep_selection) {
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeFinishComposingText");
if (!render_widget_host_)
return;
render_widget_host_->ImeFinishComposingText(keep_selection);
// Stop Monitoring for composition updates after we are done.
RequestImeCompositionUpdate(false);
}
void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeCancelComposition");
if (!render_widget_host_)
return;
render_widget_host_->ImeCancelComposition();
// Stop Monitoring for composition updates after we are done.
RequestImeCompositionUpdate(false);
}
#endif
void CefRenderWidgetHostViewOSR::SetNeedsBeginFrames(bool enabled) {
SetFrameRate();
@ -1516,3 +1554,34 @@ void CefRenderWidgetHostViewOSR::InvalidateInternal(
copy_frame_generator_->GenerateCopyFrame(true, bounds_in_pixels);
}
}
void CefRenderWidgetHostViewOSR::RequestImeCompositionUpdate(
bool start_monitoring) {
if (!render_widget_host_)
return;
render_widget_host_->Send(
new InputMsg_RequestCompositionUpdate(render_widget_host_->GetRoutingID(),
false, start_monitoring));
}
void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) {
if (browser_impl_.get()) {
CefRange cef_range(range.start(), range.end());
CefRenderHandler::RectList rcList;
for (size_t i = 0; i < character_bounds.size(); ++i) {
rcList.push_back(CefRect(character_bounds[i].x(), character_bounds[i].y(),
character_bounds[i].width(),
character_bounds[i].height()));
}
CefRefPtr<CefRenderHandler> handler =
browser_impl_->GetClient()->GetRenderHandler();
if (handler.get()) {
handler->OnImeCompositionRangeChanged(browser_impl_->GetBrowser(),
cef_range, rcList);
}
}
}

View File

@ -101,7 +101,6 @@ class CefRenderWidgetHostViewOSR
gfx::Vector2dF GetLastScrollOffset() const override;
gfx::NativeView GetNativeView() const override;
gfx::NativeViewAccessible GetNativeViewAccessible() override;
ui::TextInputClient* GetTextInputClient() override;
void Focus() override;
bool HasFocus() const override;
bool IsSurfaceAvailableForCopy() const override;
@ -133,23 +132,13 @@ class CefRenderWidgetHostViewOSR
content::RenderWidgetHostView* reference_host_view) override;
void UpdateCursor(const content::WebCursor& cursor) override;
void SetIsLoading(bool is_loading) override;
void TextInputStateChanged(const content::TextInputState& params) override;
void ImeCancelComposition() override;
void RenderProcessGone(base::TerminationStatus status,
int error_code) override;
void Destroy() override;
void SetTooltipText(const base::string16& tooltip_text) override;
#if defined(OS_MACOSX)
void SelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) override;
#endif
gfx::Size GetRequestedRendererSize() const override;
gfx::Size GetPhysicalBackingSize() const override;
void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) override;
void CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
@ -177,12 +166,9 @@ class CefRenderWidgetHostViewOSR
void ShowDisambiguationPopup(const gfx::Rect& rect_pixels,
const SkBitmap& zoomed_bitmap) override;
#endif
#if defined(OS_MACOSX) || defined(USE_AURA)
void ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) override;
#endif
void SetNeedsBeginFrames(bool enabled) override;
@ -233,19 +219,16 @@ class CefRenderWidgetHostViewOSR
return popup_type_ != blink::WebPopupTypeNone;
}
#if defined(OS_MACOSX)
NSTextInputContext* GetNSTextInputContext();
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent);
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent);
bool GetCachedFirstRectForCharacterRange(gfx::Range range, gfx::Rect* rect,
gfx::Range* actual_range) const;
const std::string& selected_text() const { return selected_text_; }
const gfx::Range& composition_range() const { return composition_range_; }
const base::string16& selection_text() const { return selection_text_; }
size_t selection_text_offset() const { return selection_text_offset_; }
#endif // defined(OS_MACOSX)
void ImeSetComposition(
const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range);
void ImeCommitText(const CefString& text,
const CefRange& replacement_range,
int relative_cursor_pos);
void ImeFinishComposingText(bool keep_selection);
void ImeCancelComposition();
void AddGuestHostView(CefRenderWidgetHostViewOSR* guest_host);
void RemoveGuestHostView(CefRenderWidgetHostViewOSR* guest_host);
@ -293,30 +276,11 @@ class CefRenderWidgetHostViewOSR
void InvalidateInternal(const gfx::Rect& bounds_in_pixels);
void RequestImeCompositionUpdate(bool start_monitoring);
#if defined(OS_MACOSX)
friend class MacHelper;
// Returns composition character boundary rectangle. The |range| is
// composition based range. Also stores |actual_range| which is corresponding
// to actually used range for returned rectangle.
gfx::Rect GetFirstRectForCompositionRange(const gfx::Range& range,
gfx::Range* actual_range) const;
// Converts from given whole character range to composition oriented range. If
// the conversion failed, return gfx::Range::InvalidRange.
gfx::Range ConvertCharacterRangeToCompositionRange(
const gfx::Range& request_range) const;
// Returns true if there is line break in |range| and stores line breaking
// point to |line_breaking_point|. The |line_break_point| is valid only if
// this function returns true.
static bool GetLineBreakIndex(const std::vector<gfx::Rect>& bounds,
const gfx::Range& range,
size_t* line_break_point);
void DestroyNSTextInputOSR();
#endif // defined(OS_MACOSX)
#endif
void PlatformCreateCompositorWidget();
void PlatformResizeCompositorWidget(const gfx::Size& size);
void PlatformDestroyCompositorWidget();
@ -384,23 +348,6 @@ class CefRenderWidgetHostViewOSR
gfx::Vector2dF last_scroll_offset_;
bool is_scroll_offset_changed_pending_;
#if defined(OS_MACOSX)
NSTextInputContext* text_input_context_osr_mac_;
// Selected text on the renderer.
std::string selected_text_;
// The current composition character range and its bounds.
gfx::Range composition_range_;
std::vector<gfx::Rect> composition_bounds_;
// The current caret bounds.
gfx::Rect caret_rect_;
// The current first selection bounds.
gfx::Rect first_selection_rect_;
#endif
base::WeakPtrFactory<CefRenderWidgetHostViewOSR> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CefRenderWidgetHostViewOSR);

View File

@ -12,7 +12,6 @@
#import <Cocoa/Cocoa.h>
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/osr/text_input_client_osr_mac.h"
#include "base/compiler_specific.h"
#include "base/strings/utf_string_conversions.h"
@ -20,17 +19,6 @@
#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
#include "ui/events/latency_info.h"
namespace {
CefTextInputClientOSRMac* GetInputClientFromContext(
const NSTextInputContext* context) {
if (!context)
return NULL;
return reinterpret_cast<CefTextInputClientOSRMac*>([context client]);
}
} // namespace
class MacHelper :
public content::BrowserCompositorMacClient,
@ -131,226 +119,6 @@ bool CefRenderWidgetHostViewOSR::IsSpeaking() const {
void CefRenderWidgetHostViewOSR::StopSpeaking() {
}
void CefRenderWidgetHostViewOSR::TextInputStateChanged(
const content::TextInputState& params) {
[NSApp updateWindows];
}
void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
CefTextInputClientOSRMac* client = GetInputClientFromContext(
text_input_context_osr_mac_);
if (client)
[client cancelComposition];
}
void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
const gfx::Range& range,
const std::vector<gfx::Rect>& character_bounds) {
CefTextInputClientOSRMac* client = GetInputClientFromContext(
text_input_context_osr_mac_);
if (!client)
return;
composition_range_ = range;
composition_bounds_ = character_bounds;
}
void CefRenderWidgetHostViewOSR::SelectionChanged(
const base::string16& text,
size_t offset,
const gfx::Range& range) {
if (range.is_empty() || text.empty()) {
selected_text_.clear();
} else {
size_t pos = range.GetMin() - offset;
size_t n = range.length();
DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
if (pos >= text.length()) {
DCHECK(false) << "The text can not cover range.";
return;
}
selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
}
RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
}
void CefRenderWidgetHostViewOSR::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) {
if (params.anchor_rect == params.focus_rect)
caret_rect_ = params.anchor_rect;
first_selection_rect_ = params.anchor_rect;
}
CefTextInputContext CefRenderWidgetHostViewOSR::GetNSTextInputContext() {
if (!text_input_context_osr_mac_) {
CefTextInputClientOSRMac* text_input_client_osr_mac =
[[CefTextInputClientOSRMac alloc] initWithRenderWidgetHostViewOSR:
this];
text_input_context_osr_mac_ = [[NSTextInputContext alloc] initWithClient:
text_input_client_osr_mac];
}
return text_input_context_osr_mac_;
}
void CefRenderWidgetHostViewOSR::HandleKeyEventBeforeTextInputClient(
CefEventHandle keyEvent) {
CefTextInputClientOSRMac* client = GetInputClientFromContext(
text_input_context_osr_mac_);
if (client)
[client HandleKeyEventBeforeTextInputClient: keyEvent];
}
void CefRenderWidgetHostViewOSR::HandleKeyEventAfterTextInputClient(
CefEventHandle keyEvent) {
CefTextInputClientOSRMac* client = GetInputClientFromContext(
text_input_context_osr_mac_);
if (client)
[client HandleKeyEventAfterTextInputClient: keyEvent];
}
bool CefRenderWidgetHostViewOSR::GetCachedFirstRectForCharacterRange(
gfx::Range range, gfx::Rect* rect, gfx::Range* actual_range) const {
DCHECK(rect);
const gfx::Range requested_range(range);
// If requested range is same as caret location, we can just return it.
if (selection_range_.is_empty() && requested_range == selection_range_) {
if (actual_range)
*actual_range = range;
*rect = caret_rect_;
return true;
}
if (composition_range_.is_empty()) {
if (!selection_range_.Contains(requested_range))
return false;
if (actual_range)
*actual_range = selection_range_;
*rect = first_selection_rect_;
return true;
}
const gfx::Range request_range_in_composition =
ConvertCharacterRangeToCompositionRange(requested_range);
if (request_range_in_composition == gfx::Range::InvalidRange())
return false;
// If firstRectForCharacterRange in WebFrame is failed in renderer,
// ImeCompositionRangeChanged will be sent with empty vector.
if (composition_bounds_.empty())
return false;
DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
gfx::Range ui_actual_range;
*rect = GetFirstRectForCompositionRange(request_range_in_composition,
&ui_actual_range);
if (actual_range) {
*actual_range = gfx::Range(
composition_range_.start() + ui_actual_range.start(),
composition_range_.start() + ui_actual_range.end());
}
return true;
}
gfx::Rect CefRenderWidgetHostViewOSR::GetFirstRectForCompositionRange(
const gfx::Range& range, gfx::Range* actual_range) const {
DCHECK(actual_range);
DCHECK(!composition_bounds_.empty());
DCHECK(range.start() <= composition_bounds_.size());
DCHECK(range.end() <= composition_bounds_.size());
if (range.is_empty()) {
*actual_range = range;
if (range.start() == composition_bounds_.size()) {
return gfx::Rect(composition_bounds_[range.start() - 1].right(),
composition_bounds_[range.start() - 1].y(),
0,
composition_bounds_[range.start() - 1].height());
} else {
return gfx::Rect(composition_bounds_[range.start()].x(),
composition_bounds_[range.start()].y(),
0,
composition_bounds_[range.start()].height());
}
}
size_t end_idx;
if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
end_idx = range.end();
}
*actual_range = gfx::Range(range.start(), end_idx);
gfx::Rect rect = composition_bounds_[range.start()];
for (size_t i = range.start() + 1; i < end_idx; ++i) {
rect.Union(composition_bounds_[i]);
}
return rect;
}
gfx::Range CefRenderWidgetHostViewOSR::ConvertCharacterRangeToCompositionRange(
const gfx::Range& request_range) const {
if (composition_range_.is_empty())
return gfx::Range::InvalidRange();
if (request_range.is_reversed())
return gfx::Range::InvalidRange();
if (request_range.start() < composition_range_.start() ||
request_range.start() > composition_range_.end() ||
request_range.end() > composition_range_.end()) {
return gfx::Range::InvalidRange();
}
return gfx::Range(
request_range.start() - composition_range_.start(),
request_range.end() - composition_range_.start());
}
bool CefRenderWidgetHostViewOSR::GetLineBreakIndex(
const std::vector<gfx::Rect>& bounds,
const gfx::Range& range,
size_t* line_break_point) {
DCHECK(line_break_point);
if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
return false;
// We can't check line breaking completely from only rectangle array. Thus we
// assume the line breaking as the next character's y offset is larger than
// a threshold. Currently the threshold is determined as minimum y offset plus
// 75% of maximum height.
const size_t loop_end_idx =
std::min(bounds.size(), static_cast<size_t>(range.end()));
int max_height = 0;
int min_y_offset = std::numeric_limits<int32_t>::max();
for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
max_height = std::max(max_height, bounds[idx].height());
min_y_offset = std::min(min_y_offset, bounds[idx].y());
}
int line_break_threshold = min_y_offset + (max_height * 3 / 4);
for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
if (bounds[idx].y() > line_break_threshold) {
*line_break_point = idx;
return true;
}
}
return false;
}
void CefRenderWidgetHostViewOSR::DestroyNSTextInputOSR() {
CefTextInputClientOSRMac* client = GetInputClientFromContext(
text_input_context_osr_mac_);
if (client) {
[client release];
client = NULL;
}
[text_input_context_osr_mac_ release];
text_input_context_osr_mac_ = NULL;
}
ui::Compositor* CefRenderWidgetHostViewOSR::GetCompositor() const {
return browser_compositor_->GetCompositor();
}

View File

@ -1,79 +0,0 @@
// Copyright (c) 2013 The Chromium Embedded Framework 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 CEF_LIBCEF_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
#define CEF_LIBCEF_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
#pragma once
#import <Cocoa/Cocoa.h>
#include <vector>
#include "libcef/browser/osr/render_widget_host_view_osr.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/string16.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/common/edit_command.h"
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
// Implementation for the NSTextInputClient protocol used for enabling IME on
// mac when window rendering is disabled.
@interface CefTextInputClientOSRMac : NSObject<NSTextInputClient> {
@private
// Represents the input-method attributes supported by this object.
base::scoped_nsobject<NSArray> validAttributesForMarkedText_;
// Indicates if we are currently handling a key down event.
BOOL handlingKeyDown_;
// Indicates if there is any marked text.
BOOL hasMarkedText_;
// Indicates whether there was any marked text prior to handling
// the current key event.
BOOL oldHasMarkedText_;
// Indicates if unmarkText is called or not when handling a keyboard
// event.
BOOL unmarkTextCalled_;
// 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.
base::string16 textToBeInserted_;
// Marked text which was generated by handling a key down event.
base::string16 markedText_;
// Underline information of the |markedText_|.
std::vector<blink::WebCompositionUnderline> underlines_;
// Replacement range information received from |setMarkedText:|.
gfx::Range setMarkedTextReplacementRange_;
// Indicates if doCommandBySelector method receives any edit command when
// handling a key down event.
BOOL hasEditCommands_;
// Contains edit commands received by the -doCommandBySelector: method when
// handling a key down event, not including inserting commands, eg. insertTab,
// etc.
content::EditCommands editCommands_;
CefRenderWidgetHostViewOSR* renderWidgetHostView_;
}
@property(nonatomic, readonly) NSRange selectedRange;
@property(nonatomic) BOOL handlingKeyDown;
- (id)initWithRenderWidgetHostViewOSR:(CefRenderWidgetHostViewOSR*) rwhv;
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent;
- (void)HandleKeyEventAfterTextInputClient:(NSEvent*)keyEvent;
- (void)cancelComposition;
@end
#endif // CEF_LIBCEF_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_

View File

@ -1,402 +0,0 @@
// Copyright (c) 2013 The Chromium Embedded Framework 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 "libcef/browser/osr/text_input_client_osr_mac.h"
#include "libcef/browser/browser_host_impl.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/sys_string_conversions.h"
#import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
#import "content/browser/renderer_host/text_input_client_mac.h"
#include "content/common/input_messages.h"
namespace {
// TODO(suzhe): Upstream this function.
blink::WebColor WebColorFromNSColor(NSColor *color) {
CGFloat r, g, b, a;
[color getRed:&r green:&g blue:&b alpha:&a];
return
std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8 |
std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
}
// Extract underline information from an attributed string. Mostly copied from
// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
void ExtractUnderlines(NSAttributedString* string,
std::vector<blink::WebCompositionUnderline>* underlines) {
int length = [[string string] length];
int i = 0;
while (i < length) {
NSRange range;
NSDictionary* attrs = [string attributesAtIndex:i
longestEffectiveRange:&range
inRange:NSMakeRange(i, length - i)];
NSNumber *style = [attrs objectForKey: NSUnderlineStyleAttributeName];
if (style) {
blink::WebColor color = SK_ColorBLACK;
if (NSColor *colorAttr =
[attrs objectForKey:NSUnderlineColorAttributeName]) {
color = WebColorFromNSColor(
[colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
}
underlines->push_back(blink::WebCompositionUnderline(
range.location, NSMaxRange(range), color, [style intValue] > 1, 0));
}
i = range.location + range.length;
}
}
} // namespace
extern "C" {
extern NSString* NSTextInputReplacementRangeAttributeName;
}
@implementation CefTextInputClientOSRMac
@synthesize selectedRange = selectedRange_;
@synthesize handlingKeyDown = handlingKeyDown_;
- (id)initWithRenderWidgetHostViewOSR:(CefRenderWidgetHostViewOSR*)rwhv {
self = [super init];
renderWidgetHostView_ = rwhv;
return self;
}
- (NSArray*)validAttributesForMarkedText {
if (!validAttributesForMarkedText_) {
validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
NSUnderlineStyleAttributeName,
NSUnderlineColorAttributeName,
NSMarkedClauseSegmentAttributeName,
NSTextInputReplacementRangeAttributeName,
nil]);
}
return validAttributesForMarkedText_.get();
}
- (NSRange)markedRange {
return hasMarkedText_ ?
renderWidgetHostView_->composition_range().ToNSRange() :
NSMakeRange(NSNotFound, 0);
}
- (BOOL)hasMarkedText {
return hasMarkedText_;
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
NSString* im_text = isAttributedString ? [aString string] : aString;
if (handlingKeyDown_) {
textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
} else {
gfx::Range replacement_range(replacementRange);
renderWidgetHostView_->render_widget_host()->ImeCommitText(
base::SysNSStringToUTF16(im_text), replacement_range, 0);
}
// Inserting text will delete all marked text automatically.
hasMarkedText_ = NO;
}
- (void)doCommandBySelector:(SEL)aSelector {
// An input method calls this function to dispatch an editing command to be
// handled by this view.
if (aSelector == @selector(noop:))
return;
std::string command([content::RenderWidgetHostViewMacEditCommandHelper::
CommandNameForSelector(aSelector) UTF8String]);
// If this method is called when handling a key down event, then we need to
// handle the command in the key event handler. Otherwise we can just handle
// it here.
if (handlingKeyDown_) {
hasEditCommands_ = YES;
// We ignore commands that insert characters, because this was causing
// strange behavior (e.g. tab always inserted a tab rather than moving to
// the next field on the page).
if (!base::StartsWith(command, "insert", base::CompareCase::SENSITIVE))
editCommands_.push_back(content::EditCommand(command, ""));
} else {
renderWidgetHostView_->render_widget_host()->Send(
new InputMsg_ExecuteEditCommand(
renderWidgetHostView_->render_widget_host()->GetRoutingID(),
command, ""));
}
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelRange
replacementRange:(NSRange)replacementRange {
// An input method updates the composition string.
// We send the given text and range to the renderer so it can update the
// composition node of WebKit.
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
NSString* im_text = isAttributedString ? [aString string] : aString;
int length = [im_text length];
// |markedRange_| will get set on a callback from ImeSetComposition().
selectedRange_ = newSelRange;
markedText_ = base::SysNSStringToUTF16(im_text);
hasMarkedText_ = (length > 0);
underlines_.clear();
if (isAttributedString) {
ExtractUnderlines(aString, &underlines_);
} else {
// Use a thin black underline by default.
underlines_.push_back(blink::WebCompositionUnderline(0, length,
SK_ColorBLACK, false, 0));
}
// If we are handling a key down event, then SetComposition() will be
// called in keyEvent: method.
// Input methods of Mac use setMarkedText calls with an empty text to cancel
// an ongoing composition. So, we should check whether or not the given text
// is empty to update the input method state. (Our input method backend can
// automatically cancels an ongoing composition when we send an empty text.
// So, it is OK to send an empty text to the renderer.)
if (handlingKeyDown_) {
setMarkedTextReplacementRange_ = gfx::Range(replacementRange);
} else if (!handlingKeyDown_) {
renderWidgetHostView_->render_widget_host()->ImeSetComposition(
markedText_, underlines_, gfx::Range(replacementRange),
newSelRange.location, NSMaxRange(newSelRange));
}
}
- (void)unmarkText {
// Delete the composition node of the renderer and finish an ongoing
// composition.
// It seems an input method calls the setMarkedText method and set an empty
// text when it cancels an ongoing composition, i.e. I have never seen an
// input method calls this method.
hasMarkedText_ = NO;
markedText_.clear();
underlines_.clear();
// If we are handling a key down event, then FinishComposingText() will be
// called in keyEvent: method.
if (!handlingKeyDown_) {
renderWidgetHostView_->render_widget_host()->ImeFinishComposingText(false);
} else {
unmarkTextCalled_ = YES;
}
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
actualRange:(NSRangePointer)actualRange {
if (actualRange)
*actualRange = range;
const gfx::Range requested_range(range);
if (requested_range.is_reversed())
return nil;
gfx::Range expected_range;
const base::string16* expected_text;
if (!renderWidgetHostView_->composition_range().is_empty()) {
expected_text = &markedText_;
expected_range = renderWidgetHostView_->composition_range();
} else {
expected_text = &renderWidgetHostView_->selection_text();
size_t offset = renderWidgetHostView_->selection_text_offset();
expected_range = gfx::Range(offset, offset + expected_text->size());
}
if (!expected_range.Contains(requested_range))
return nil;
// Gets the raw bytes to avoid unnecessary string copies for generating
// NSString.
const base::char16* bytes =
&(*expected_text)[requested_range.start() - expected_range.start()];
// Avoid integer overflow.
base::CheckedNumeric<size_t> requested_len = requested_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]);
return [[[NSAttributedString alloc] initWithString:ns_string] autorelease];
}
- (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
actualRange:(NSRangePointer)actualRange {
NSRect rect;
gfx::Rect gfxRect;
gfx::Range range(theRange);
gfx::Range actual_range;
if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(range,
&gfxRect, &actual_range)) {
rect = content::TextInputClientMac::GetInstance()->
GetFirstRectForRange(renderWidgetHostView_->GetRenderWidgetHost(),
range.ToNSRange());
if (actualRange)
*actualRange = range.ToNSRange();
} else {
rect = NSRectFromCGRect(gfxRect.ToCGRect());
}
return rect;
}
- (NSRect) screenRectFromViewRect:(NSRect)rect {
NSRect screenRect;
int screenX, screenY;
renderWidgetHostView_->browser_impl()->GetClient()->GetRenderHandler()->
GetScreenPoint(renderWidgetHostView_->browser_impl()->GetBrowser(),
rect.origin.x, rect.origin.y, screenX, screenY);
screenRect.origin = NSMakePoint(screenX, screenY);
screenRect.size = rect.size;
return screenRect;
}
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
actualRange:(NSRangePointer)actualRange {
NSRect rect = [self firstViewRectForCharacterRange:theRange
actualRange:actualRange];
// Convert into screen coordinates for return.
rect = [self screenRectFromViewRect:rect];
if (rect.origin.y >= rect.size.height)
rect.origin.y -= rect.size.height;
else
rect.origin.y = 0;
return rect;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
// |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.
CefRect view_rect;
renderWidgetHostView_->browser_impl()->GetClient()->GetRenderHandler()->
GetViewRect(renderWidgetHostView_->browser_impl()->GetBrowser(),
view_rect);
thePoint.x -= view_rect.x;
thePoint.y -= view_rect.y;
thePoint.y = view_rect.height - thePoint.y;
NSUInteger index = content::TextInputClientMac::GetInstance()->
GetCharacterIndexAtPoint(renderWidgetHostView_->GetRenderWidgetHost(),
gfx::Point(thePoint.x, thePoint.y));
return index;
}
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent {
DCHECK([keyEvent type] == NSKeyDown);
// Don't call this method recursively.
DCHECK(!handlingKeyDown_);
oldHasMarkedText_ = hasMarkedText_;
handlingKeyDown_ = YES;
// These variables might be set when handling the keyboard event.
// Clear them here so that we can know whether they have changed afterwards.
textToBeInserted_.clear();
markedText_.clear();
underlines_.clear();
setMarkedTextReplacementRange_ = gfx::Range::InvalidRange();
unmarkTextCalled_ = NO;
hasEditCommands_ = NO;
editCommands_.clear();
}
- (void)HandleKeyEventAfterTextInputClient:(NSEvent*)keyEvent {
handlingKeyDown_ = NO;
// Then send keypress and/or composition related events.
// If there was a marked text or the text to be inserted is longer than 1
// character, then we send the text by calling FinishComposingText().
// Otherwise, if the text to be inserted only contains 1 character, then we
// can just send a keypress event which is fabricated by changing the type of
// the keydown event, so that we can retain all necessary informations, such
// as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
// prevent the browser from handling it again.
// Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
// handle BMP characters here, as we can always insert non-BMP characters as
// text.
if (!hasMarkedText_ && !oldHasMarkedText_ &&
textToBeInserted_.length() <= 1) {
content::NativeWebKeyboardEvent event(keyEvent);
if (textToBeInserted_.length() == 1) {
event.type = blink::WebInputEvent::Type::Char;
event.text[0] = textToBeInserted_[0];
event.text[1] = 0;
}
renderWidgetHostView_->SendKeyEvent(event);
}
BOOL textInserted = NO;
if (textToBeInserted_.length() >
((hasMarkedText_ || oldHasMarkedText_) ? 0u : 1u)) {
renderWidgetHostView_->render_widget_host()->ImeCommitText(
textToBeInserted_, gfx::Range::InvalidRange(), 0);
textToBeInserted_ = YES;
}
// Updates or cancels the composition. If some text has been inserted, then
// we don't need to cancel the composition explicitly.
if (hasMarkedText_ && markedText_.length()) {
// Sends the updated marked text to the renderer so it can update the
// composition node in WebKit.
// When marked text is available, |selectedRange_| will be the range being
// selected inside the marked text.
renderWidgetHostView_->render_widget_host()->ImeSetComposition(
markedText_, underlines_, setMarkedTextReplacementRange_,
selectedRange_.location, NSMaxRange(selectedRange_));
} else if (oldHasMarkedText_ && !hasMarkedText_ && !textInserted) {
if (unmarkTextCalled_) {
renderWidgetHostView_->render_widget_host()->ImeFinishComposingText(
false);
} else {
renderWidgetHostView_->render_widget_host()->ImeCancelComposition();
}
}
setMarkedTextReplacementRange_ = gfx::Range::InvalidRange();
}
- (void)cancelComposition {
if (!hasMarkedText_)
return;
// Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
// doesn't call any NSTextInput functions, such as setMarkedText or
// insertText. So, we need to send an IPC message to a renderer so it can
// delete the composition node.
// TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
// NSTextInputContext. http://www.crbug.com/479010.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
[currentInputManager markedTextAbandoned:self];
#pragma clang diagnostic pop
hasMarkedText_ = NO;
// Should not call [self unmarkText] here, because it'll send unnecessary
// cancel composition IPC message to the renderer.
}
@end

View File

@ -781,46 +781,96 @@ void CEF_CALLBACK browser_host_set_windowless_frame_rate(
frame_rate);
}
cef_text_input_context_t CEF_CALLBACK browser_host_get_nstext_input_context(
void CEF_CALLBACK browser_host_ime_set_composition(
struct _cef_browser_host_t* self, const cef_string_t* text,
size_t underlinesCount, cef_composition_underline_t const* underlines,
const cef_range_t* replacement_range,
const cef_range_t* selection_range) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: replacement_range; type: simple_byref_const
DCHECK(replacement_range);
if (!replacement_range)
return;
// Verify param: selection_range; type: simple_byref_const
DCHECK(selection_range);
if (!selection_range)
return;
// Unverified params: text, underlines
// Translate param: underlines; type: simple_vec_byref_const
std::vector<CefCompositionUnderline > underlinesList;
if (underlinesCount > 0) {
for (size_t i = 0; i < underlinesCount; ++i) {
CefCompositionUnderline underlinesVal = underlines[i];
underlinesList.push_back(underlinesVal);
}
}
// Translate param: replacement_range; type: simple_byref_const
CefRange replacement_rangeVal = replacement_range?*replacement_range:CefRange(
);
// Translate param: selection_range; type: simple_byref_const
CefRange selection_rangeVal = selection_range?*selection_range:CefRange();
// Execute
CefBrowserHostCppToC::Get(self)->ImeSetComposition(
CefString(text),
underlinesList,
replacement_rangeVal,
selection_rangeVal);
}
void CEF_CALLBACK browser_host_ime_commit_text(struct _cef_browser_host_t* self,
const cef_string_t* text, const cef_range_t* replacement_range,
int relative_cursor_pos) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: replacement_range; type: simple_byref_const
DCHECK(replacement_range);
if (!replacement_range)
return;
// Unverified params: text
// Translate param: replacement_range; type: simple_byref_const
CefRange replacement_rangeVal = replacement_range?*replacement_range:CefRange(
);
// Execute
CefBrowserHostCppToC::Get(self)->ImeCommitText(
CefString(text),
replacement_rangeVal,
relative_cursor_pos);
}
void CEF_CALLBACK browser_host_ime_finish_composing_text(
struct _cef_browser_host_t* self, int keep_selection) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Execute
CefBrowserHostCppToC::Get(self)->ImeFinishComposingText(
keep_selection?true:false);
}
void CEF_CALLBACK browser_host_ime_cancel_composition(
struct _cef_browser_host_t* self) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return NULL;
// Execute
cef_text_input_context_t _retval = CefBrowserHostCppToC::Get(
self)->GetNSTextInputContext();
// Return type: simple
return _retval;
}
void CEF_CALLBACK browser_host_handle_key_event_before_text_input_client(
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Execute
CefBrowserHostCppToC::Get(self)->HandleKeyEventBeforeTextInputClient(
keyEvent);
}
void CEF_CALLBACK browser_host_handle_key_event_after_text_input_client(
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Execute
CefBrowserHostCppToC::Get(self)->HandleKeyEventAfterTextInputClient(
keyEvent);
CefBrowserHostCppToC::Get(self)->ImeCancelComposition();
}
void CEF_CALLBACK browser_host_drag_target_drag_enter(
@ -1008,11 +1058,11 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() {
browser_host_get_windowless_frame_rate;
GetStruct()->set_windowless_frame_rate =
browser_host_set_windowless_frame_rate;
GetStruct()->get_nstext_input_context = browser_host_get_nstext_input_context;
GetStruct()->handle_key_event_before_text_input_client =
browser_host_handle_key_event_before_text_input_client;
GetStruct()->handle_key_event_after_text_input_client =
browser_host_handle_key_event_after_text_input_client;
GetStruct()->ime_set_composition = browser_host_ime_set_composition;
GetStruct()->ime_commit_text = browser_host_ime_commit_text;
GetStruct()->ime_finish_composing_text =
browser_host_ime_finish_composing_text;
GetStruct()->ime_cancel_composition = browser_host_ime_cancel_composition;
GetStruct()->drag_target_drag_enter = browser_host_drag_target_drag_enter;
GetStruct()->drag_target_drag_over = browser_host_drag_target_drag_over;
GetStruct()->drag_target_drag_leave = browser_host_drag_target_drag_leave;

View File

@ -350,6 +350,46 @@ void CEF_CALLBACK render_handler_on_scroll_offset_changed(
y);
}
void CEF_CALLBACK render_handler_on_ime_composition_range_changed(
struct _cef_render_handler_t* self, cef_browser_t* browser,
const cef_range_t* selected_range, size_t character_boundsCount,
cef_rect_t const* character_bounds) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Verify param: selected_range; type: simple_byref_const
DCHECK(selected_range);
if (!selected_range)
return;
// Verify param: character_bounds; type: simple_vec_byref_const
DCHECK(character_boundsCount == 0 || character_bounds);
if (character_boundsCount > 0 && !character_bounds)
return;
// Translate param: selected_range; type: simple_byref_const
CefRange selected_rangeVal = selected_range?*selected_range:CefRange();
// Translate param: character_bounds; type: simple_vec_byref_const
std::vector<CefRect > character_boundsList;
if (character_boundsCount > 0) {
for (size_t i = 0; i < character_boundsCount; ++i) {
CefRect character_boundsVal = character_bounds[i];
character_boundsList.push_back(character_boundsVal);
}
}
// Execute
CefRenderHandlerCppToC::Get(self)->OnImeCompositionRangeChanged(
CefBrowserCToCpp::Wrap(browser),
selected_rangeVal,
character_boundsList);
}
} // namespace
@ -368,6 +408,8 @@ CefRenderHandlerCppToC::CefRenderHandlerCppToC() {
GetStruct()->update_drag_cursor = render_handler_update_drag_cursor;
GetStruct()->on_scroll_offset_changed =
render_handler_on_scroll_offset_changed;
GetStruct()->on_ime_composition_range_changed =
render_handler_on_ime_composition_range_changed;
}
template<> CefRefPtr<CefRenderHandler> CefCppToC<CefRenderHandlerCppToC,

View File

@ -667,44 +667,81 @@ void CefBrowserHostCToCpp::SetWindowlessFrameRate(int frame_rate) {
frame_rate);
}
CefTextInputContext CefBrowserHostCToCpp::GetNSTextInputContext() {
void CefBrowserHostCToCpp::ImeSetComposition(const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range, const CefRange& selection_range) {
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, get_nstext_input_context))
return NULL;
if (CEF_MEMBER_MISSING(_struct, ime_set_composition))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
cef_text_input_context_t _retval = _struct->get_nstext_input_context(_struct);
// Unverified params: text, underlines
// Return type: simple
return _retval;
// Translate param: underlines; type: simple_vec_byref_const
const size_t underlinesCount = underlines.size();
cef_composition_underline_t* underlinesList = NULL;
if (underlinesCount > 0) {
underlinesList = new cef_composition_underline_t[underlinesCount];
DCHECK(underlinesList);
if (underlinesList) {
for (size_t i = 0; i < underlinesCount; ++i) {
underlinesList[i] = underlines[i];
}
}
}
// Execute
_struct->ime_set_composition(_struct,
text.GetStruct(),
underlinesCount,
underlinesList,
&replacement_range,
&selection_range);
// Restore param:underlines; type: simple_vec_byref_const
if (underlinesList)
delete [] underlinesList;
}
void CefBrowserHostCToCpp::HandleKeyEventBeforeTextInputClient(
CefEventHandle keyEvent) {
void CefBrowserHostCToCpp::ImeCommitText(const CefString& text,
const CefRange& replacement_range, int relative_cursor_pos) {
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, handle_key_event_before_text_input_client))
if (CEF_MEMBER_MISSING(_struct, ime_commit_text))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Unverified params: text
// Execute
_struct->ime_commit_text(_struct,
text.GetStruct(),
&replacement_range,
relative_cursor_pos);
}
void CefBrowserHostCToCpp::ImeFinishComposingText(bool keep_selection) {
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, ime_finish_composing_text))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
_struct->handle_key_event_before_text_input_client(_struct,
keyEvent);
_struct->ime_finish_composing_text(_struct,
keep_selection);
}
void CefBrowserHostCToCpp::HandleKeyEventAfterTextInputClient(
CefEventHandle keyEvent) {
void CefBrowserHostCToCpp::ImeCancelComposition() {
cef_browser_host_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, handle_key_event_after_text_input_client))
if (CEF_MEMBER_MISSING(_struct, ime_cancel_composition))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
_struct->handle_key_event_after_text_input_client(_struct,
keyEvent);
_struct->ime_cancel_composition(_struct);
}
void CefBrowserHostCToCpp::DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,

View File

@ -86,9 +86,14 @@ class CefBrowserHostCToCpp
void NotifyMoveOrResizeStarted() OVERRIDE;
int GetWindowlessFrameRate() OVERRIDE;
void SetWindowlessFrameRate(int frame_rate) OVERRIDE;
CefTextInputContext GetNSTextInputContext() OVERRIDE;
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) OVERRIDE;
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) OVERRIDE;
void ImeSetComposition(const CefString& text,
const std::vector<CefCompositionUnderline>& underlines,
const CefRange& replacement_range,
const CefRange& selection_range) OVERRIDE;
void ImeCommitText(const CefString& text, const CefRange& replacement_range,
int relative_cursor_pos) OVERRIDE;
void ImeFinishComposingText(bool keep_selection) OVERRIDE;
void ImeCancelComposition() OVERRIDE;
void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
const CefMouseEvent& event, DragOperationsMask allowed_ops) OVERRIDE;
void DragTargetDragOver(const CefMouseEvent& event,

View File

@ -283,6 +283,45 @@ void CefRenderHandlerCToCpp::OnScrollOffsetChanged(
y);
}
void CefRenderHandlerCToCpp::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser, const CefRange& selected_range,
const RectList& character_bounds) {
cef_render_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_ime_composition_range_changed))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Translate param: character_bounds; type: simple_vec_byref_const
const size_t character_boundsCount = character_bounds.size();
cef_rect_t* character_boundsList = NULL;
if (character_boundsCount > 0) {
character_boundsList = new cef_rect_t[character_boundsCount];
DCHECK(character_boundsList);
if (character_boundsList) {
for (size_t i = 0; i < character_boundsCount; ++i) {
character_boundsList[i] = character_bounds[i];
}
}
}
// Execute
_struct->on_ime_composition_range_changed(_struct,
CefBrowserCppToC::Wrap(browser),
&selected_range,
character_boundsCount,
character_boundsList);
// Restore param:character_bounds; type: simple_vec_byref_const
if (character_boundsList)
delete [] character_boundsList;
}
// CONSTRUCTOR - Do not edit by hand.

View File

@ -51,6 +51,9 @@ class CefRenderHandlerCToCpp
DragOperation operation) override;
void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser, double x,
double y) override;
void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
const CefRange& selected_range,
const RectList& character_bounds) override;
};
#endif // BUILDING_CEF_SHARED

View File

@ -1204,6 +1204,13 @@ void BrowserWindowOsrGtk::UpdateDragCursor(
CEF_REQUIRE_UI_THREAD();
}
void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) {
CEF_REQUIRE_UI_THREAD();
}
void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
REQUIRE_MAIN_THREAD();
DCHECK(!glarea_);

View File

@ -76,6 +76,10 @@ class BrowserWindowOsrGtk : public BrowserWindow,
int x, int y) OVERRIDE;
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
CefRenderHandler::DragOperation operation) OVERRIDE;
void OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
private:
// Create the GTK GlArea.

View File

@ -9,6 +9,7 @@
#include "cefclient/browser/browser_window.h"
#include "cefclient/browser/client_handler_osr.h"
#include "cefclient/browser/osr_renderer.h"
#include "cefclient/browser/text_input_client_osr_mac.h"
namespace client {
@ -77,6 +78,10 @@ class BrowserWindowOsrMac : public BrowserWindow,
int x, int y) OVERRIDE;
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
CefRenderHandler::DragOperation operation) OVERRIDE;
void OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
private:
// Create the NSView.

View File

@ -14,6 +14,18 @@
#include "cefclient/browser/bytes_write_handler.h"
#include "cefclient/browser/geometry_util.h"
#include "cefclient/browser/main_message_loop.h"
#include "cefclient/browser/text_input_client_osr_mac.h"
namespace {
CefTextInputClientOSRMac* GetInputClientFromContext(
const NSTextInputContext* context) {
if (!context)
return NULL;
return reinterpret_cast<CefTextInputClientOSRMac*>([context client]);
}
} // namespace
@interface BrowserOpenGLView
: NSOpenGLView <NSDraggingSource, NSDraggingDestination> {
@ -29,13 +41,16 @@
float device_scale_factor_;
// Drag and drop
// Drag and drop.
CefRefPtr<CefDragData> current_drag_data_;
NSDragOperation current_drag_op_;
NSDragOperation current_allowed_ops_;
NSPasteboard* pasteboard_;
CFStringRef fileUTI_;
// For intreacting with IME.
NSTextInputContext* text_input_context_osr_mac_;
// Event monitor for scroll wheel end event.
id endWheelMonitor_;
}
@ -81,7 +96,8 @@
- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint;
- (NSRect)convertRectFromBackingInternal:(NSRect)aRect;
- (NSRect)convertRectToBackingInternal:(NSRect)aRect;
- (void)ChangeCompositionRange:(CefRange)range
character_bounds:(const CefRenderHandler::RectList&) character_bounds;
@end
@ -315,18 +331,25 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
- (void)keyDown:(NSEvent*)event {
CefRefPtr<CefBrowser> browser = [self getBrowser];
if (!browser.get())
if (!browser.get() || !text_input_context_osr_mac_)
return;
if ([event type] != NSFlagsChanged) {
browser->GetHost()->HandleKeyEventBeforeTextInputClient(event);
CefTextInputClientOSRMac* client = GetInputClientFromContext(
text_input_context_osr_mac_);
// The return value of this method seems to always be set to YES,
// thus we ignore it and ask the host view whether IME is active
// or not.
[[self inputContext] handleEvent:event];
if (client) {
[client HandleKeyEventBeforeTextInputClient:event];
browser->GetHost()->HandleKeyEventAfterTextInputClient(event);
// The return value of this method seems to always be set to YES, thus we
// ignore it and ask the host view whether IME is active or not.
[text_input_context_osr_mac_ handleEvent:event];
CefKeyEvent keyEvent;
[self getKeyEvent:keyEvent forEvent:event];
[client HandleKeyEventAfterTextInputClient:keyEvent];
}
}
}
@ -502,10 +525,15 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
}
- (NSTextInputContext*)inputContext {
CefRefPtr<CefBrowser> browser = [self getBrowser];
if (browser.get())
return browser->GetHost()->GetNSTextInputContext();
return NULL;
if (!text_input_context_osr_mac_) {
CefTextInputClientOSRMac* text_input_client =
[[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]];
text_input_context_osr_mac_ = [[NSTextInputContext alloc] initWithClient:
text_input_client];
}
return text_input_context_osr_mac_;
}
- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event {
@ -1098,6 +1126,13 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
return [self convertRectToBacking:aRect];
}
- (void)ChangeCompositionRange:(CefRange)range
character_bounds:(const CefRenderHandler::RectList&) bounds {
CefTextInputClientOSRMac* client =
GetInputClientFromContext(text_input_context_osr_mac_);
if (client)
[client ChangeCompositionRange: range character_bounds:bounds];
}
@end
@ -1437,6 +1472,18 @@ void BrowserWindowOsrMac::UpdateDragCursor(
[GLView(nsview_) setCurrentDragOp:operation];
}
void BrowserWindowOsrMac::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& bounds) {
CEF_REQUIRE_UI_THREAD();
if (nsview_) {
[GLView(nsview_) ChangeCompositionRange:selection_range
character_bounds:bounds];
}
}
void BrowserWindowOsrMac::Create(ClientWindowHandle parent_handle,
const CefRect& rect) {
REQUIRE_MAIN_THREAD();

View File

@ -135,4 +135,15 @@ void ClientHandlerOsr::UpdateDragCursor(CefRefPtr<CefBrowser> browser,
osr_delegate_->UpdateDragCursor(browser, operation);
}
void ClientHandlerOsr::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) {
CEF_REQUIRE_UI_THREAD();
if (!osr_delegate_)
return;
osr_delegate_->OnImeCompositionRangeChanged(browser, selection_range,
character_bounds);
}
} // namespace client

View File

@ -56,6 +56,10 @@ class ClientHandlerOsr : public ClientHandler,
virtual void UpdateDragCursor(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::DragOperation operation) = 0;
virtual void OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) = 0;
protected:
virtual ~OsrDelegate() {}
@ -109,6 +113,10 @@ class ClientHandlerOsr : public ClientHandler,
int x, int y) OVERRIDE;
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
CefRenderHandler::DragOperation operation) OVERRIDE;
void OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
private:
// Only accessed on the UI thread.

View File

@ -0,0 +1,390 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2013 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.
// Implementation based on ui/base/ime/win/imm32_manager.cc from Chromium.
#include <windowsx.h>
#include <msctf.h>
#include "include/base/cef_build.h"
#include "cefclient/browser/geometry_util.h"
#include "cefclient/browser/main_message_loop.h"
#include "cefclient/browser/resource.h"
#include "cefclient/browser/util_win.h"
#include "cefclient/browser/osr_ime_handler_win.h"
#define ColorUNDERLINE 0xFF000000 // Black SkColor value for underline,
// same as Blink.
#define ColorBKCOLOR 0x00000000 // White SkColor value for background,
// same as Blink.
namespace client {
namespace {
// Determines whether or not the given attribute represents a selection
bool IsSelectionAttribute(char attribute) {
return (attribute == ATTR_TARGET_CONVERTED ||
attribute == ATTR_TARGET_NOTCONVERTED);
}
// Helper function for OsrImeHandlerWin::GetCompositionInfo() method,
// to get the target range that's selected by the user in the current
// composition string.
void GetCompositionSelectionRange(HIMC imc, int* target_start,
int* target_end) {
int attribute_size = ::ImmGetCompositionString(imc, GCS_COMPATTR, NULL, 0);
if (attribute_size > 0) {
int start = 0;
int end = 0;
std::vector<char> attribute_data(attribute_size);
::ImmGetCompositionString(imc, GCS_COMPATTR, &attribute_data[0],
attribute_size);
for (start = 0; start < attribute_size; ++start) {
if (IsSelectionAttribute(attribute_data[start]))
break;
}
for (end = start; end < attribute_size; ++end) {
if (!IsSelectionAttribute(attribute_data[end]))
break;
}
*target_start = start;
*target_end = end;
}
}
// Helper function for OsrImeHandlerWin::GetCompositionInfo() method, to get
// underlines information of the current composition string.
void GetCompositionUnderlines(
HIMC imc,
int target_start,
int target_end,
std::vector<CefCompositionUnderline> &underlines) {
int clause_size = ::ImmGetCompositionString(imc, GCS_COMPCLAUSE, NULL, 0);
int clause_length = clause_size / sizeof(uint32);
if (clause_length) {
std::vector<uint32> clause_data(clause_length);
::ImmGetCompositionString(imc, GCS_COMPCLAUSE,
&clause_data[0], clause_size);
for (int i = 0; i < clause_length - 1; ++i) {
cef_composition_underline_t underline;
underline.range.from = clause_data[i];
underline.range.to = clause_data[i + 1];
underline.color = ColorUNDERLINE;
underline.background_color = ColorBKCOLOR;
underline.thick = 0;
// Use thick underline for the target clause.
if (underline.range.from >= target_start &&
underline.range.to <= target_end) {
underline.thick = 1;
}
underlines.push_back(underline);
}
}
}
} // namespace
OsrImeHandlerWin::OsrImeHandlerWin(HWND hwnd)
: ime_status_(false),
hwnd_(hwnd),
input_language_id_(LANG_USER_DEFAULT),
is_composing_(false),
cursor_index_(-1),
system_caret_(false) {
ime_rect_ = { -1, -1, 0, 0 };
}
OsrImeHandlerWin::~OsrImeHandlerWin() {
DestroyImeWindow();
}
void OsrImeHandlerWin::SetInputLanguage() {
// Retrieve the current input language from the system's keyboard layout.
// Using GetKeyboardLayoutName instead of GetKeyboardLayout, because
// the language from GetKeyboardLayout is the language under where the
// keyboard layout is installed. And the language from GetKeyboardLayoutName
// indicates the language of the keyboard layout itself.
// See crbug.com/344834.
WCHAR keyboard_layout[KL_NAMELENGTH];
if (::GetKeyboardLayoutNameW(keyboard_layout)) {
input_language_id_ =
static_cast<LANGID>(_wtoi(&keyboard_layout[KL_NAMELENGTH >> 1]));
} else {
input_language_id_ = 0x0409; // Fallback to en-US.
}
}
void OsrImeHandlerWin::CreateImeWindow() {
// Chinese/Japanese IMEs somehow ignore function calls to
// ::ImmSetCandidateWindow(), and use the position of the current system
// caret instead -::GetCaretPos().
// Therefore, we create a temporary system caret for Chinese IMEs and use
// it during this input context.
// Since some third-party Japanese IME also uses ::GetCaretPos() to determine
// their window position, we also create a caret for Japanese IMEs.
if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE ||
PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) {
if (!system_caret_) {
if (::CreateCaret(hwnd_, NULL, 1, 1))
system_caret_ = true;
}
}
}
void OsrImeHandlerWin::DestroyImeWindow() {
// Destroy the system caret if we have created for this IME input context.
if (system_caret_) {
::DestroyCaret();
system_caret_ = false;
}
}
void OsrImeHandlerWin::MoveImeWindow() {
// Does nothing when the target window has no input focus.
if (GetFocus() != hwnd_)
return;
CefRect rc = ime_rect_;
int location = cursor_index_;
// If location is not specified fall back to the composition range start.
if (location == -1)
location = composition_range_.from;
// Offset location by the composition range start if required.
if (location >= composition_range_.from)
location -= composition_range_.from;
if (location < static_cast<int>(composition_bounds_.size()))
rc = composition_bounds_[location];
else
return;
HIMC imc = ::ImmGetContext(hwnd_);
if (imc) {
const int kCaretMargin = 1;
if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE) {
// Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
// when a user disables TSF (Text Service Framework) and CUAS (Cicero
// Unaware Application Support).
// On the other hand, when a user enables TSF and CUAS, Chinese IMEs
// ignore the position of the current system caret and use the
// parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
// parameter CFS_CANDIDATEPOS.
// Therefore, we do not only call ::ImmSetCandidateWindow() but also
// set the positions of the temporary system caret if it exists.
CANDIDATEFORM candidate_position = {
0, CFS_CANDIDATEPOS, { rc.x, rc.y }, { 0, 0, 0, 0 }
};
::ImmSetCandidateWindow(imc, &candidate_position);
}
if (system_caret_) {
switch (PRIMARYLANGID(input_language_id_)) {
case LANG_JAPANESE:
::SetCaretPos(rc.x, rc.y + rc.height);
break;
default:
::SetCaretPos(rc.x, rc.y);
break;
}
}
if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
// Korean IMEs require the lower-left corner of the caret to move their
// candidate windows.
rc.y += kCaretMargin;
}
// Japanese IMEs and Korean IMEs also use the rectangle given to
// ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
// Therefore, we also set this parameter here.
CANDIDATEFORM exclude_rectangle = {
0, CFS_EXCLUDE, { rc.x, rc.y },
{ rc.x, rc.y, rc.x + rc.width, rc.y + rc.height }
};
::ImmSetCandidateWindow(imc, &exclude_rectangle);
::ImmReleaseContext(hwnd_, imc);
}
}
void OsrImeHandlerWin::CleanupComposition() {
// Notify the IMM attached to the given window to complete the ongoing
// composition (when given window is de-activated while composing and
// re-activated) and reset the composition status.
if (is_composing_) {
HIMC imc = ::ImmGetContext(hwnd_);
if (imc) {
::ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
::ImmReleaseContext(hwnd_, imc);
}
ResetComposition();
}
}
void OsrImeHandlerWin::ResetComposition() {
// Reset the composition status.
is_composing_ = false;
cursor_index_ = -1;
}
void OsrImeHandlerWin::GetCompositionInfo(
HIMC imc,
LPARAM lparam,
CefString &composition_text,
std::vector<CefCompositionUnderline> &underlines,
int& composition_start) {
// We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and
// convert them into underlines and selection range respectively.
underlines.clear();
int length = static_cast<int>(composition_text.length());
// Find out the range selected by the user.
int target_start = length;
int target_end = length;
if (lparam & GCS_COMPATTR)
GetCompositionSelectionRange(imc, &target_start, &target_end);
// Retrieve the selection range information. If CS_NOMOVECARET is specified
// it means the cursor should not be moved and we therefore place the caret at
// the beginning of the composition string. Otherwise we should honour the
// GCS_CURSORPOS value if it's available.
// TODO(suzhe): Due to a bug in WebKit we currently can't use selection range
// with composition string.
// See: https://bugs.webkit.org/show_bug.cgi?id=40805
if (!(lparam & CS_NOMOVECARET) && (lparam & GCS_CURSORPOS)) {
// IMM32 does not support non-zero-width selection in a composition. So
// always use the caret position as selection range.
int cursor = ::ImmGetCompositionString(imc, GCS_CURSORPOS, NULL, 0);
composition_start = cursor;
} else {
composition_start = 0;
}
// Retrieve the clause segmentations and convert them to underlines.
if (lparam & GCS_COMPCLAUSE)
GetCompositionUnderlines(imc, target_start, target_end, underlines);
// Set default underlines in case there is no clause information.
if (!underlines.size()) {
CefCompositionUnderline underline;
underline.color = ColorUNDERLINE;
underline.background_color = ColorBKCOLOR;
if (target_start > 0) {
underline.range.from = 0;
underline.range.to = target_start;
underline.thick = 0;
underlines.push_back(underline);
}
if (target_end > target_start) {
underline.range.from = target_start;
underline.range.to = target_end;
underline.thick = 1;
underlines.push_back(underline);
}
if (target_end < length) {
underline.range.from = target_end;
underline.range.to = length;
underline.thick = 0;
underlines.push_back(underline);
}
}
}
bool OsrImeHandlerWin::GetString(HIMC imc, WPARAM lparam, int type,
CefString& result) {
if (!(lparam & type))
return false;
LONG string_size = ::ImmGetCompositionString(imc, type, NULL, 0);
if (string_size <= 0)
return false;
// For trailing NULL - ImmGetCompositionString excludes that.
string_size += sizeof(WCHAR);
std::vector<wchar_t> buffer(string_size);
::ImmGetCompositionString(imc, type, &buffer[0], string_size);
result.FromWString(&buffer[0]);
return true;
}
bool OsrImeHandlerWin::GetResult(LPARAM lparam, CefString& result) {
bool ret = false;
HIMC imc = ::ImmGetContext(hwnd_);
if (imc) {
ret = GetString(imc, lparam, GCS_RESULTSTR, result);
::ImmReleaseContext(hwnd_, imc);
}
return ret;
}
bool OsrImeHandlerWin::GetComposition(
LPARAM lparam,
CefString &composition_text,
std::vector<CefCompositionUnderline> &underlines,
int& composition_start) {
bool ret = false;
HIMC imc = ::ImmGetContext(hwnd_);
if (imc) {
// Copy the composition string to the CompositionText object.
ret = GetString(imc, lparam, GCS_COMPSTR, composition_text);
if (ret) {
// Retrieve the composition underlines and selection range information.
GetCompositionInfo(imc, lparam, composition_text, underlines,
composition_start);
// Mark that there is an ongoing composition.
is_composing_ = true;
}
::ImmReleaseContext(hwnd_, imc);
}
return ret;
}
void OsrImeHandlerWin::DisableIME() {
CleanupComposition();
::ImmAssociateContextEx(hwnd_, NULL, 0);
}
void OsrImeHandlerWin::CancelIME() {
if (is_composing_) {
HIMC imc = ::ImmGetContext(hwnd_);
if (imc) {
::ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
::ImmReleaseContext(hwnd_, imc);
}
ResetComposition();
}
}
void OsrImeHandlerWin::EnableIME() {
// Load the default IME context.
::ImmAssociateContextEx(hwnd_, NULL, IACE_DEFAULT);
}
void OsrImeHandlerWin::UpdateCaretPosition(int index) {
// Save the caret position.
cursor_index_ = index;
// Move the IME window.
MoveImeWindow();
}
void OsrImeHandlerWin::ChangeCompositionRange(
const CefRange& selection_range,
const std::vector<CefRect>& bounds) {
composition_range_ = selection_range;
composition_bounds_ = bounds;
MoveImeWindow();
}
} // namespace client

View File

@ -0,0 +1,116 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2013 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 CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
#pragma once
#include <windows.h>
#include <vector>
#include "include/internal/cef_types_wrappers.h"
namespace client {
// Handles IME for the native parent window that hosts an off-screen browser.
// This object is only accessed on the CEF UI thread.
class OsrImeHandlerWin {
public:
explicit OsrImeHandlerWin(HWND hwnd);
virtual ~OsrImeHandlerWin();
// Retrieves whether or not there is an ongoing composition.
bool is_composing() const { return is_composing_; }
// Retrieves the input language from Windows and update it.
void SetInputLanguage();
// Creates the IME caret windows if required.
void CreateImeWindow();
// Destroys the IME caret windows.
void DestroyImeWindow();
// Cleans up the all resources attached to the given IMM32Manager object, and
// reset its composition status.
void CleanupComposition();
// Resets the composition status and cancels the ongoing composition.
void ResetComposition();
// Retrieves a composition result of the ongoing composition if it exists.
bool GetResult(LPARAM lparam, CefString& result);
// Retrieves the current composition status of the ongoing composition.
// Includes composition text, underline information and selection range in the
// composition text. IMM32 does not support char selection.
bool GetComposition(LPARAM lparam, CefString &composition_text,
std::vector<CefCompositionUnderline> &underlines,
int& composition_start);
// Enables the IME attached to the given window.
virtual void EnableIME();
// Disables the IME attached to the given window.
virtual void DisableIME();
// Cancels an ongoing composition of the IME.
virtual void CancelIME();
// Updates the IME caret position of the given window.
void UpdateCaretPosition(int index);
// Updates the composition range. |selected_range| is the range of characters
// that have been selected. |character_bounds| is the bounds of each character
// in view device coordinates.
void ChangeCompositionRange(const CefRange& selection_range,
const std::vector<CefRect>& character_bounds);
// Updates the position of the IME windows.
void MoveImeWindow();
private:
// Retrieves the composition information.
void GetCompositionInfo(HIMC imm_context, LPARAM lparam,
CefString &composition_text,
std::vector<CefCompositionUnderline>& underlines,
int& composition_start);
// Retrieves a string from the IMM.
bool GetString(HIMC imm_context, WPARAM lparam, int type, CefString& result);
// Represents whether or not there is an ongoing composition.
bool is_composing_;
// The current composition character range and its bounds.
std::vector<CefRect> composition_bounds_;
// This value represents whether or not the current input context has IMEs.
bool ime_status_;
// The current input Language ID retrieved from Windows -
// used for processing language-specific operations in IME.
LANGID input_language_id_;
// Represents whether or not the current input context has created a system
// caret to set the position of its IME candidate window.
bool system_caret_;
// The rectangle of the input caret retrieved from a renderer process.
CefRect ime_rect_;
// The current cursor index in composition string.
int cursor_index_;
// The composition range in the string. This may be used to determine the
// offset in composition bounds.
CefRange composition_range_;
// Hwnd associated with this instance.
HWND hwnd_;
};
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_

View File

@ -11,6 +11,7 @@
#include "cefclient/browser/main_message_loop.h"
#include "cefclient/browser/resource.h"
#include "cefclient/browser/util_win.h"
#include "cefclient/browser/osr_ime_handler_win.h"
namespace client {
@ -55,7 +56,6 @@ OsrWindowWin::OsrWindowWin(Delegate* delegate,
hwnd_(NULL),
hdc_(NULL),
hrc_(NULL),
client_rect_(),
device_scale_factor_(client::GetDeviceScaleFactor()),
painting_popup_(false),
render_task_pending_(false),
@ -255,6 +255,8 @@ void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {
DCHECK_EQ(register_res, S_OK);
#endif
ime_handler_.reset(new OsrImeHandlerWin(hwnd_));
// Notify the window owner.
NotifyNativeWindowCreated(hwnd_);
}
@ -273,6 +275,7 @@ void OsrWindowWin::Destroy() {
// Destroy the native window.
::DestroyWindow(hwnd_);
ime_handler_.reset();
hwnd_ = NULL;
}
@ -390,6 +393,74 @@ void OsrWindowWin::RegisterOsrClass(HINSTANCE hInstance,
RegisterClassEx(&wcex);
}
void OsrWindowWin::OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam) {
// We handle the IME Composition Window ourselves (but let the IME Candidates
// Window be handled by IME through DefWindowProc()), so clear the
// ISC_SHOWUICOMPOSITIONWINDOW flag:
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
::DefWindowProc(hwnd_, message, wParam, lParam);
// Create Caret Window if required
if (ime_handler_) {
ime_handler_->CreateImeWindow();
ime_handler_->MoveImeWindow();
}
}
void OsrWindowWin::OnIMEStartComposition() {
if (ime_handler_) {
ime_handler_->CreateImeWindow();
ime_handler_->MoveImeWindow();
ime_handler_->ResetComposition();
}
}
void OsrWindowWin::OnIMEComposition(UINT message, WPARAM wParam,
LPARAM lParam) {
if (browser_ && ime_handler_) {
CefString cTextStr;
if (ime_handler_->GetResult(lParam, cTextStr)) {
// Send the text to the browser. The |replacement_range| and
// |relative_cursor_pos| params are not used on Windows, so provide
// default invalid values.
browser_->GetHost()->ImeCommitText(cTextStr,
CefRange(UINT32_MAX, UINT32_MAX), 0);
ime_handler_->ResetComposition();
// Continue reading the composition string - Japanese IMEs send both
// GCS_RESULTSTR and GCS_COMPSTR.
}
std::vector<CefCompositionUnderline> underlines;
int composition_start = 0;
if (ime_handler_->GetComposition(lParam, cTextStr, underlines,
composition_start)) {
// Send the composition string to the browser. The |replacement_range|
// param is not used on Windows, so provide a default invalid value.
browser_->GetHost()->ImeSetComposition(cTextStr, underlines,
CefRange(UINT32_MAX, UINT32_MAX),
CefRange(composition_start,
static_cast<int>(composition_start + cTextStr.length())));
// Update the Candidate Window position. The cursor is at the end so
// subtract 1. This is safe because IMM32 does not support non-zero-width
// in a composition. Also, negative values are safely ignored in
// MoveImeWindow
ime_handler_->UpdateCaretPosition(composition_start - 1);
} else {
OnIMECancelCompositionEvent();
}
}
}
void OsrWindowWin::OnIMECancelCompositionEvent() {
if (browser_ && ime_handler_) {
browser_->GetHost()->ImeCancelComposition();
ime_handler_->ResetComposition();
ime_handler_->DestroyImeWindow();
}
}
// static
LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
@ -399,7 +470,22 @@ LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, UINT message,
if (!self)
return DefWindowProc(hWnd, message, wParam, lParam);
// We want to handle IME events before the OS does any default handling.
switch (message) {
case WM_IME_SETCONTEXT:
self->OnIMESetContext(message, wParam, lParam);
return 0;
case WM_IME_STARTCOMPOSITION:
self->OnIMEStartComposition();
return 0;
case WM_IME_COMPOSITION:
self->OnIMEComposition(message, wParam, lParam);
return 0;
case WM_IME_ENDCOMPOSITION:
self->OnIMECancelCompositionEvent();
// Let WTL call::DefWindowProc() and release its resources.
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
@ -909,6 +995,24 @@ void OsrWindowWin::UpdateDragCursor(
#endif
}
void OsrWindowWin::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) {
CEF_REQUIRE_UI_THREAD();
if (ime_handler_) {
// Convert from view coordinates to device coordinates.
CefRenderHandler::RectList device_bounds;
CefRenderHandler::RectList::const_iterator it = character_bounds.begin();
for (; it != character_bounds.end(); ++it) {
device_bounds.push_back(LogicalToDevice(*it, device_scale_factor_));
}
ime_handler_->ChangeCompositionRange(selection_range, device_bounds);
}
}
#if defined(CEF_USE_ATL)
CefBrowserHost::DragOperationsMask

View File

@ -16,6 +16,8 @@
namespace client {
class OsrImeHandlerWin;
// Represents the native parent window for an off-screen browser. This object
// must live on the CEF UI thread in order to handle CefRenderHandler callbacks.
// The methods of this class are thread-safe unless otherwise indicated.
@ -94,6 +96,11 @@ class OsrWindowWin :
void OnPaint();
bool OnEraseBkgnd();
void OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam);
void OnIMEStartComposition();
void OnIMEComposition(UINT message, WPARAM wParam, LPARAM lParam);
void OnIMECancelCompositionEvent();
// Manage popup bounds.
bool IsOverPopupWidget(int x, int y) const;
int GetPopupXOffset() const;
@ -133,6 +140,10 @@ class OsrWindowWin :
int x, int y) OVERRIDE;
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
CefRenderHandler::DragOperation operation) OVERRIDE;
void OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selection_range,
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
#if defined(CEF_USE_ATL)
// OsrDragEvents methods.
@ -156,6 +167,9 @@ class OsrWindowWin :
HDC hdc_;
HGLRC hrc_;
// Class that encapsulates IMM32 APIs and controls IMEs attached to a window.
scoped_ptr<OsrImeHandlerWin> ime_handler_;
RECT client_rect_;
float device_scale_factor_;

View File

@ -0,0 +1,77 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2013 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 CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
#pragma once
#import <Cocoa/Cocoa.h>
#include <vector>
#include <string>
#include "include/cef_browser.h"
#include "include/cef_render_handler.h"
// Implementation for the NSTextInputClient protocol used for enabling IME on
// mac when window rendering is disabled.
@interface CefTextInputClientOSRMac : NSObject<NSTextInputClient> {
@private
// The range of current marked text inside the whole content of the DOM node
// being edited.
NSRange markedRange_;
// The current composition character range and its bounds.
CefRange composition_range_;
std::vector<CefRect> composition_bounds_;
// Represents the input-method attributes supported by this object.
NSArray* validAttributesForMarkedText_;
// Indicates if we are currently handling a key down event.
BOOL handlingKeyDown_;
// Indicates if there is any marked text.
BOOL hasMarkedText_;
// Indicates whether there was any marked text prior to handling
// the current key event.
BOOL oldHasMarkedText_;
// Indicates if unmarkText is called or not when handling a keyboard
// event.
BOOL unmarkTextCalled_;
// 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.
std::string textToBeInserted_;
// Marked text which was generated by handling a key down event.
CefString markedText_;
// Underline information of the |markedText_|.
std::vector<CefCompositionUnderline> underlines_;
// Replacement range information received from |setMarkedText:|.
CefRange setMarkedTextReplacementRange_;
CefRefPtr<CefBrowser> browser_;
}
@property(nonatomic, readonly) NSRange selectedRange;
@property(nonatomic) BOOL handlingKeyDown;
- (id)initWithBrowser:(CefRefPtr<CefBrowser>)browser;
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent;
- (void)HandleKeyEventAfterTextInputClient:(CefKeyEvent)keyEvent;
- (void)ChangeCompositionRange:(CefRange)range
character_bounds:(const CefRenderHandler::RectList&)bounds;
- (void)cancelComposition;
@end
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_

View File

@ -0,0 +1,343 @@
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
// 2013 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.
// Implementation based on
// content/browser/renderer_host/render_widget_host_view_mac.mm from Chromium.
#include "text_input_client_osr_mac.h"
#include "include/cef_client.h"
#define ColorBLACK 0xFF000000 // Same as Blink SKColor.
namespace {
// TODO(suzhe): Upstream this function.
cef_color_t CefColorFromNSColor(NSColor *color) {
CGFloat r, g, b, a;
[color getRed:&r green:&g blue:&b alpha:&a];
return
std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8 |
std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
}
// Extract underline information from an attributed string. Mostly copied from
// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
void ExtractUnderlines(NSAttributedString* string,
std::vector<CefCompositionUnderline>* underlines) {
int length = [[string string] length];
int i = 0;
while (i < length) {
NSRange range;
NSDictionary* attrs = [string attributesAtIndex:i
longestEffectiveRange:&range
inRange:NSMakeRange(i, length - i)];
NSNumber *style = [attrs objectForKey: NSUnderlineStyleAttributeName];
if (style) {
cef_color_t color = ColorBLACK;
if (NSColor *colorAttr =
[attrs objectForKey:NSUnderlineColorAttributeName]) {
color = CefColorFromNSColor(
[colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
}
cef_composition_underline_t line = {
{range.location, NSMaxRange(range)}, color, 0, [style intValue] > 1
};
underlines->push_back(line);
}
i = range.location + range.length;
}
}
} // namespace
extern "C" {
extern NSString* NSTextInputReplacementRangeAttributeName;
}
@implementation CefTextInputClientOSRMac
@synthesize selectedRange = selectedRange_;
@synthesize handlingKeyDown = handlingKeyDown_;
- (id)initWithBrowser:(CefRefPtr<CefBrowser>)browser {
self = [super init];
browser_ = browser;
return self;
}
- (NSArray*)validAttributesForMarkedText {
if (!validAttributesForMarkedText_) {
validAttributesForMarkedText_ = [[NSArray alloc] initWithObjects:
NSUnderlineStyleAttributeName,
NSUnderlineColorAttributeName,
NSMarkedClauseSegmentAttributeName,
NSTextInputReplacementRangeAttributeName,
nil];
}
return validAttributesForMarkedText_;
}
- (NSRange)markedRange {
return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
}
- (BOOL)hasMarkedText {
return hasMarkedText_;
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
NSString* im_text = isAttributedString ? [aString string] : aString;
if (handlingKeyDown_) {
textToBeInserted_.append([im_text UTF8String]);
} else {
cef_range_t range = {
replacementRange.location,
NSMaxRange(replacementRange)
};
browser_->GetHost()->ImeCommitText([im_text UTF8String], range, 0);
}
// Inserting text will delete all marked text automatically.
hasMarkedText_ = NO;
}
- (void)doCommandBySelector:(SEL)aSelector {
// An input method calls this function to dispatch an editing command to be
// handled by this view.
}
- (void)setMarkedText:(id)aString
selectedRange:(NSRange)newSelRange
replacementRange:(NSRange)replacementRange {
// An input method has updated the composition string. We send the given text
// and range to the browser so it can update the composition node of Blink.
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
NSString* im_text = isAttributedString ? [aString string] : aString;
int length = [im_text length];
// |markedRange_| will get set in a callback from ImeSetComposition().
selectedRange_ = newSelRange;
markedText_ = [im_text UTF8String];
hasMarkedText_ = (length > 0);
underlines_.clear();
if (isAttributedString) {
ExtractUnderlines(aString, &underlines_);
} else {
// Use a thin black underline by default.
cef_composition_underline_t line = {
{0, length}, ColorBLACK, 0, false
};
underlines_.push_back(line);
}
// If we are handling a key down event then ImeSetComposition() will be
// called from the keyEvent: method.
// Input methods of Mac use setMarkedText calls with empty text to cancel an
// ongoing composition. Our input method backend will automatically cancel an
// ongoing composition when we send empty text.
if (handlingKeyDown_) {
setMarkedTextReplacementRange_ = {
replacementRange.location,
NSMaxRange(replacementRange)
};
} else if (!handlingKeyDown_) {
CefRange replacement_range(replacementRange.location,
NSMaxRange(replacementRange));
CefRange selection_range(newSelRange.location, NSMaxRange(newSelRange));
browser_->GetHost()->ImeSetComposition(
markedText_, underlines_, replacement_range, selection_range);
}
}
- (void)unmarkText {
// Delete the composition node of the browser and finish an ongoing
// composition.
// It seems that, instead of calling this method, an input method will call
// the setMarkedText method with empty text to cancel ongoing composition.
// Implement this method even though we don't expect it to be called.
hasMarkedText_ = NO;
markedText_.clear();
underlines_.clear();
// If we are handling a key down event then ImeFinishComposingText() will be
// called from the keyEvent: method.
if (!handlingKeyDown_)
browser_->GetHost()->ImeFinishComposingText(false);
else
unmarkTextCalled_ = YES;
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
actualRange:(NSRangePointer)actualRange {
// Modify the attributed string if required.
// Not implemented here as we do not want to control the IME window view.
return nil;
}
- (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
actualRange:(NSRangePointer)actualRange {
NSRect rect;
NSUInteger location = theRange.location;
// If location is not specified fall back to the composition range start.
if (location == NSNotFound)
location = markedRange_.location;
// Offset location by the composition range start if required.
if (location >= markedRange_.location)
location -= markedRange_.location;
if(location < composition_bounds_.size()) {
const CefRect& rc = composition_bounds_[location];
rect = NSMakeRect(rc.x, rc.y, rc.width, rc.height);
}
if (actualRange)
*actualRange = NSMakeRange(location, theRange.length);
return rect;
}
- (NSRect)screenRectFromViewRect:(NSRect)rect {
NSRect screenRect;
int screenX, screenY;
browser_->GetHost()->GetClient()->GetRenderHandler()->GetScreenPoint(
browser_, rect.origin.x, rect.origin.y, screenX, screenY);
screenRect.origin = NSMakePoint(screenX, screenY);
screenRect.size = rect.size;
return screenRect;
}
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
actualRange:(NSRangePointer)actualRange {
NSRect rect = [self firstViewRectForCharacterRange:theRange
actualRange:actualRange];
// Convert into screen coordinates for return.
rect = [self screenRectFromViewRect:rect];
if (rect.origin.y >= rect.size.height)
rect.origin.y -= rect.size.height;
else
rect.origin.y = 0;
return rect;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
return NSNotFound;
}
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent {
DCHECK([keyEvent type] == NSKeyDown);
// Don't call this method recursively.
DCHECK(!handlingKeyDown_);
oldHasMarkedText_ = hasMarkedText_;
handlingKeyDown_ = YES;
// These variables might be set when handling the keyboard event.
// Clear them here so that we can know whether they have changed afterwards.
textToBeInserted_.clear();
markedText_.clear();
underlines_.clear();
setMarkedTextReplacementRange_ = CefRange(UINT32_MAX, UINT32_MAX);
unmarkTextCalled_ = NO;
}
- (void)HandleKeyEventAfterTextInputClient:(CefKeyEvent)keyEvent {
handlingKeyDown_ = NO;
// Send keypress and/or composition related events.
// Note that |textToBeInserted_| is a UTF-16 string but it's fine to only
// handle BMP characters here as we can always insert non-BMP characters as
// text.
// If the text to be inserted only contains 1 character then we can just send
// a keypress event.
if (!hasMarkedText_ && !oldHasMarkedText_ &&
textToBeInserted_.length() <= 1) {
keyEvent.type = KEYEVENT_KEYDOWN;
browser_->GetHost()->SendKeyEvent(keyEvent);
// Don't send a CHAR event for non-char keys like arrows, function keys and
// clear.
if (keyEvent.modifiers & (EVENTFLAG_IS_KEY_PAD)) {
if(keyEvent.native_key_code == 71)
return;
}
keyEvent.type = KEYEVENT_CHAR;
browser_->GetHost()->SendKeyEvent(keyEvent);
}
// If the text to be inserted contains multiple characters then send the text
// to the browser using ImeCommitText().
BOOL textInserted = NO;
if (textToBeInserted_.length() >
((hasMarkedText_ || oldHasMarkedText_) ? 0u : 1u)) {
browser_->GetHost()->ImeCommitText(
textToBeInserted_, CefRange(UINT32_MAX, UINT32_MAX), 0);
textToBeInserted_.clear();
}
// Update or cancel the composition. If some text has been inserted then we
// don't need to explicitly cancel the composition.
if (hasMarkedText_ && markedText_.length()) {
// Update the composition by sending marked text to the browser.
// |selectedRange_| is the range being selected inside the marked text.
browser_->GetHost()->ImeSetComposition(
markedText_, underlines_, setMarkedTextReplacementRange_,
CefRange(selectedRange_.location, NSMaxRange(selectedRange_)));
} else if (oldHasMarkedText_ && !hasMarkedText_ && !textInserted) {
// There was no marked text or inserted text. Complete or cancel the
// composition.
if (unmarkTextCalled_)
browser_->GetHost()->ImeFinishComposingText(false);
else
browser_->GetHost()->ImeCancelComposition();
}
setMarkedTextReplacementRange_ = CefRange(UINT32_MAX, UINT32_MAX);
}
- (void)ChangeCompositionRange:(CefRange)range
character_bounds:(const CefRenderHandler::RectList&) bounds {
composition_range_ = range;
markedRange_ = NSMakeRange(range.from, range.to - range.from);
composition_bounds_ = bounds;
}
- (void)cancelComposition {
if (!hasMarkedText_)
return;
// Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
// doesn't call any NSTextInput functions, such as setMarkedText or
// insertText.
// TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
// NSTextInputContext. http://www.crbug.com/479010.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSInputManager* currentInputManager = [NSInputManager currentInputManager];
[currentInputManager markedTextAbandoned:self];
#pragma clang diagnostic pop
hasMarkedText_ = NO;
// Should not call [self unmarkText] here because it'll send unnecessary
// cancel composition messages to the browser.
}
@end

View File

@ -173,6 +173,14 @@ enum OSRTestType {
OSR_TEST_DRAG_DROP_UPDATE_CURSOR,
// dropping element inside drop region will move the element
OSR_TEST_DRAG_DROP_DROP,
// IMESetComposition will update the composition range
OSR_TEST_IME_SET_COMPOSITION,
// IMECommitText inserts the specified text
OSR_TEST_IME_COMMIT_TEXT,
// IMEFinishComposition will commit the text present composition text
OSR_TEST_IME_FINISH_COMPOSITION,
// IMECancelComposition will update the composition range
OSR_TEST_IME_CANCEL_COMPOSITION,
// Define the range for popup tests.
OSR_TEST_POPUP_FIRST = OSR_TEST_POPUP_PAINT,
@ -224,6 +232,27 @@ class OSRTestHandler : public RoutingTestHandler,
frame->GetURL().ToString().c_str());
DestroySucceededTestSoon();
} break;
case OSR_TEST_IME_COMMIT_TEXT: {
const std::string& expected_url =
std::string(kTestUrl) + "?k=osrimecommit";
EXPECT_STREQ(expected_url.c_str(),
frame->GetURL().ToString().c_str());
DestroySucceededTestSoon();
} break;
case OSR_TEST_IME_FINISH_COMPOSITION: {
const std::string& expected_url =
std::string(kTestUrl) + "?k=" + kKeyTestWord;
EXPECT_STREQ(expected_url.c_str(),
frame->GetURL().ToString().c_str());
DestroySucceededTestSoon();
} break;
case OSR_TEST_IME_CANCEL_COMPOSITION: {
const std::string& expected_url =
std::string(kTestUrl) + "?k=";
EXPECT_STREQ(expected_url.c_str(),
frame->GetURL().ToString().c_str());
DestroySucceededTestSoon();
} break;
default:
// Intentionally left blank
break;
@ -717,6 +746,169 @@ class OSRTestHandler : public RoutingTestHandler,
}
}
break;
case OSR_TEST_IME_COMMIT_TEXT:
{
// trigger the IME Set Composition event
if (StartTest()) {
// click inside edit box so that text could be entered
CefMouseEvent mouse_event;
mouse_event.x = MiddleX(kEditBoxRect);
mouse_event.y = MiddleY(kEditBoxRect);
mouse_event.modifiers = 0;
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, false, 1);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, true, 1);
size_t word_length = strlen(kKeyTestWord);
// Add some input keys to edit box
for (size_t i = 0; i < word_length; ++i) {
#if defined(OS_WIN)
SendKeyEvent(browser, kKeyTestWord[i]);
#elif defined(OS_MACOSX)
SendKeyEvent(browser, kKeyTestCodes[i]);
#elif defined(OS_LINUX)
SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
#else
#error "Unsupported platform"
#endif
}
// This text should be honored instead of 'ka' added via key events
CefString markedText("osrimecommit");
CefRange range(0, markedText.length());
browser->GetHost()->ImeCommitText(markedText, range, 0);
// click button to navigate
mouse_event.x = MiddleX(kNavigateButtonRect);
mouse_event.y = MiddleY(kNavigateButtonRect);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, false, 1);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, true, 1);
}
}
break;
case OSR_TEST_IME_FINISH_COMPOSITION:
{
// trigger the IME Set Composition event
if (StartTest()) {
// click inside edit box so that text could be entered
CefMouseEvent mouse_event;
mouse_event.x = MiddleX(kEditBoxRect);
mouse_event.y = MiddleY(kEditBoxRect);
mouse_event.modifiers = 0;
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
false, 1);
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
true, 1);
size_t word_length = strlen(kKeyTestWord);
// Add some input keys to edit box
for (size_t i = 0; i < word_length; ++i) {
#if defined(OS_WIN)
SendKeyEvent(browser, kKeyTestWord[i]);
#elif defined(OS_MACOSX)
SendKeyEvent(browser, kKeyTestCodes[i]);
#elif defined(OS_LINUX)
SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
#else
#error "Unsupported platform"
#endif
}
// Finish Composition should set the existing composition
browser->GetHost()->ImeFinishComposingText(true);
// click button to navigate
mouse_event.x = MiddleX(kNavigateButtonRect);
mouse_event.y = MiddleY(kNavigateButtonRect);
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
false, 1);
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
true, 1);
}
}
break;
case OSR_TEST_IME_CANCEL_COMPOSITION:
{
// trigger the IME Set Composition event
if (StartTest()) {
// click inside edit box so that text could be entered
CefMouseEvent mouse_event;
mouse_event.x = MiddleX(kEditBoxRect);
mouse_event.y = MiddleY(kEditBoxRect);
mouse_event.modifiers = 0;
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, false, 1);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, true, 1);
// Add some input keys to edit box
CefString markedText("");
std::vector<CefCompositionUnderline> underlines;
// Use a thin black underline by default.
cef_range_t range = {0, markedText.length()};
cef_composition_underline_t line = {
range, 0xFF000000, 0, false
};
underlines.push_back(line);
CefRange replacement_range(0, markedText.length());
CefRange selection_range(0,markedText.length());
// Composition should be updated
browser->GetHost()->ImeSetComposition(markedText, underlines,
replacement_range,selection_range);
// CancelComposition should clean up the edit text
browser->GetHost()->ImeCancelComposition();
// click button to navigate and verify
mouse_event.x = MiddleX(kNavigateButtonRect);
mouse_event.y = MiddleY(kNavigateButtonRect);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, false, 1);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, true, 1);
}
}
break;
case OSR_TEST_IME_SET_COMPOSITION:
{
// trigger the IME Set Composition event
if (StartTest()) {
// click inside edit box so that text could be entered
CefMouseEvent mouse_event;
mouse_event.x = MiddleX(kEditBoxRect);
mouse_event.y = MiddleY(kEditBoxRect);
mouse_event.modifiers = 0;
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, false, 1);
browser->GetHost()->SendMouseClickEvent(
mouse_event, MBT_LEFT, true, 1);
// Now set some intermediate text composition
CefString markedText("");
std::vector<CefCompositionUnderline> underlines;
// Use a thin black underline by default.
cef_range_t range = {0, markedText.length()};
cef_composition_underline_t line = {
range, 0xFF000000, 0, false
};
underlines.push_back(line);
CefRange replacement_range(0, markedText.length());
CefRange selection_range(0,markedText.length());
// This should update composition range and
// trigger the compositionRangeChanged callback
browser->GetHost()->ImeSetComposition(markedText, underlines,
replacement_range,selection_range);
}
}
break;
default:
break;
}
@ -750,6 +942,17 @@ class OSRTestHandler : public RoutingTestHandler,
}
}
void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
const CefRange& range,
const CefRenderHandler::RectList& bounds) override {
if (test_type_ == OSR_TEST_IME_SET_COMPOSITION && started()) {
EXPECT_EQ(range.from, 0);
EXPECT_EQ(range.to, 1);
EXPECT_EQ(1U, bounds.size());
DestroySucceededTestSoon();
}
}
bool StartDragging(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDragData> drag_data,
CefRenderHandler::DragOperationsMask allowed_ops,
@ -1078,3 +1281,11 @@ OSR_TEST(DragDropUpdateCursor, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 1.0f);
OSR_TEST(DragDropUpdateCursor2x, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 2.0f);
OSR_TEST(DragDropDropElement, OSR_TEST_DRAG_DROP_DROP, 1.0f);
OSR_TEST(DragDropDropElement2x, OSR_TEST_DRAG_DROP_DROP, 2.0f);
OSR_TEST(IMESetComposition, OSR_TEST_IME_SET_COMPOSITION, 1.0f);
OSR_TEST(IMESetComposition2x, OSR_TEST_IME_SET_COMPOSITION, 2.0f);
OSR_TEST(IMECommitText, OSR_TEST_IME_COMMIT_TEXT, 1.0f);
OSR_TEST(IMECommitText2x, OSR_TEST_IME_COMMIT_TEXT, 2.0f);
OSR_TEST(IMEFinishComposition, OSR_TEST_IME_FINISH_COMPOSITION, 1.0f);
OSR_TEST(IMEFinishComposition2x, OSR_TEST_IME_FINISH_COMPOSITION, 2.0f);
OSR_TEST(IMECancelComposition, OSR_TEST_IME_CANCEL_COMPOSITION, 1.0f);
OSR_TEST(IMECancelComposition2x, OSR_TEST_IME_CANCEL_COMPOSITION, 2.0f);

View File

@ -416,9 +416,10 @@ _simpletypes = {
'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'],
'cef_plugin_policy_t': ['cef_plugin_policy_t', 'PLUGIN_POLICY_ALLOW'],
'CefCursorHandle' : ['cef_cursor_handle_t', 'kNullCursorHandle'],
'CefCompositionUnderline' : ['cef_composition_underline_t',
'CefCompositionUnderline()'],
'CefEventHandle' : ['cef_event_handle_t', 'kNullEventHandle'],
'CefWindowHandle' : ['cef_window_handle_t', 'kNullWindowHandle'],
'CefTextInputContext' : ['cef_text_input_context_t' ,'NULL'],
'CefPoint' : ['cef_point_t', 'CefPoint()'],
'CefRect' : ['cef_rect_t', 'CefRect()'],
'CefSize' : ['cef_size_t', 'CefSize()'],