mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Standardize IME callbacks for off-screen rendering (issue #1675)
This commit is contained in:
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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_
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user