diff options
author | kinaba@chromium.org <kinaba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-21 07:09:32 +0000 |
---|---|---|
committer | kinaba@chromium.org <kinaba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-21 07:09:32 +0000 |
commit | 397c2396865ba5bd314827141f647a47d1cbb3fb (patch) | |
tree | a6b3972a4d3904940df315b8385aa77b625d4541 | |
parent | 455258051a2cb4591155cd099e959ce593340736 (diff) | |
download | chromium_src-397c2396865ba5bd314827141f647a47d1cbb3fb.zip chromium_src-397c2396865ba5bd314827141f647a47d1cbb3fb.tar.gz chromium_src-397c2396865ba5bd314827141f647a47d1cbb3fb.tar.bz2 |
Test for Pepper IME events.
This patch adds a way to simulate IME composition events
inside the renderer process, and tests that IME events are
properly passed between the renderer and plugins.
ppapi/tests/test_ime_input_event.cc:
is the actual test case
ppapi/{api,c,cpp}/dev/*ime_input_event_dev*:
implements an API to create IME events from plugins for testing.
other files:
wire necessary stuff for simulating IME events.
Since Pepper IME events are not delivered through WebKit/DOM layer
but rather directly sent from renderer to plugins,
the simulation part also follows the similar code path.
BUG=126714
TEST=browser_tests PPAPITest.ImeInputEvent
Review URL: https://chromiumcodereview.appspot.com/10391101
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138080 0039d316-1c4b-4281-b951-d872f2087c98
26 files changed, 1110 insertions, 133 deletions
diff --git a/chrome/test/ui/ppapi_uitest.cc b/chrome/test/ui/ppapi_uitest.cc index 51037ca..b145cca 100644 --- a/chrome/test/ui/ppapi_uitest.cc +++ b/chrome/test/ui/ppapi_uitest.cc @@ -502,6 +502,11 @@ TEST_PPAPI_OUT_OF_PROCESS(MAYBE_InputEvent) // TODO(bbudge) Enable when input events are proxied correctly for NaCl. TEST_PPAPI_NACL_VIA_HTTP(DISABLED_InputEvent) +TEST_PPAPI_IN_PROCESS(ImeInputEvent) +TEST_PPAPI_OUT_OF_PROCESS(ImeInputEvent) +// TODO(kinaba) Enable when IME events are proxied correctly for NaCl. +TEST_PPAPI_NACL_VIA_HTTP(DISABLED_ImeInputEvent) + TEST_PPAPI_IN_PROCESS(Instance_ExecuteScript); TEST_PPAPI_OUT_OF_PROCESS(Instance_ExecuteScript) // ExecuteScript isn't supported by NaCl. diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.cc b/content/renderer/pepper/pepper_plugin_delegate_impl.cc index 454fb17..67a42c8 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.cc @@ -429,6 +429,23 @@ void PepperPluginDelegateImpl::PluginSelectionChanged( render_view_->PpapiPluginSelectionChanged(); } +void PepperPluginDelegateImpl::SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) { + if (render_view_) { + render_view_->SimulateImeSetComposition( + text, underlines, selection_start, selection_end); + } +} + +void PepperPluginDelegateImpl::SimulateImeConfirmComposition( + const string16& text) { + if (render_view_) + render_view_->SimulateImeConfirmComposition(text, ui::Range()); +} + void PepperPluginDelegateImpl::OnImeSetComposition( const string16& text, const std::vector<WebKit::WebCompositionUnderline>& underlines, diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.h b/content/renderer/pepper/pepper_plugin_delegate_impl.h index 1d94723..43f20fd 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.h @@ -161,6 +161,12 @@ class PepperPluginDelegateImpl webkit::ppapi::PluginInstance* instance) OVERRIDE; virtual void PluginSelectionChanged( webkit::ppapi::PluginInstance* instance) OVERRIDE; + virtual void SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) OVERRIDE; + virtual void SimulateImeConfirmComposition(const string16& text) OVERRIDE; virtual void PluginCrashed(webkit::ppapi::PluginInstance* instance) OVERRIDE; virtual void InstanceCreated( webkit::ppapi::PluginInstance* instance) OVERRIDE; diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 6466fbe..ed32995 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -5021,6 +5021,20 @@ bool RenderViewImpl::GetPpapiPluginCaretBounds(gfx::Rect* rect) { return true; } +void RenderViewImpl::SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) { + OnImeSetComposition(text, underlines, selection_start, selection_end); +} + +void RenderViewImpl::SimulateImeConfirmComposition( + const string16& text, + const ui::Range& replacement_range) { + OnImeConfirmComposition(text, replacement_range); +} + void RenderViewImpl::PpapiPluginCancelComposition() { Send(new ViewHostMsg_ImeCancelComposition(routing_id())); ui::Range range(ui::Range::InvalidRange()); diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index fc77452a..92eeab0 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -296,6 +296,15 @@ class RenderViewImpl : public RenderWidget, // Retrieves the current caret position if a PPAPI plugin has focus. bool GetPpapiPluginCaretBounds(gfx::Rect* rect); + // Simulates IME events for testing purpose. + void SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end); + void SimulateImeConfirmComposition(const string16& text, + const ui::Range& replacement_range); + #if defined(OS_MACOSX) || defined(OS_WIN) // Informs the render view that the given plugin has gained or lost focus. void PluginFocusChanged(bool focused, int plugin_id); diff --git a/ppapi/api/dev/ppb_ime_input_event_dev.idl b/ppapi/api/dev/ppb_ime_input_event_dev.idl index 3e2deba..9e728d6 100644 --- a/ppapi/api/dev/ppb_ime_input_event_dev.idl +++ b/ppapi/api/dev/ppb_ime_input_event_dev.idl @@ -8,12 +8,60 @@ */ label Chrome { - M16 = 0.1 + M16 = 0.1, + M21 = 0.2 }; -[version=0.1, macro="PPB_IME_INPUT_EVENT_DEV_INTERFACE"] +[macro="PPB_IME_INPUT_EVENT_DEV_INTERFACE"] interface PPB_IMEInputEvent_Dev { /** + * Create() creates an IME input event with the given parameters. Normally + * you will get an IME event passed through the <code>HandleInputEvent</code> + * and will not need to create them, but some applications may want to create + * their own for internal use. + * + * @param[in] instance The instance for which this event occurred. + * + * @param[in] type A <code>PP_InputEvent_Type</code> identifying the type of + * input event. The type must be one of the IME event types. + * + * @param[in] time_stamp A <code>PP_TimeTicks</code> indicating the time + * when the event occurred. + * + * @param[in] text The string returned by <code>GetText</code>. + * + * @param[in] segment_number The number returned by + * <code>GetSegmentNumber</code>. + * + * @param[in] segment_offsets The array of numbers returned by + * <code>GetSegmentOffset</code>. If <code>segment_number</code> is zero, + * the number of elements of the array should be zero. If + * <code>segment_number</code> is non-zero, the length of the array must be + * <code>segment_number</code> + 1. + * + * @param[in] target_segment The number returned by + * <code>GetTargetSegment</code>. + * + * @param[in] selection_start The start index returned by + * <code>GetSelection</code>. + * + * @param[in] selection_end The end index returned by + * <code>GetSelection</code>. + * + * @return A <code>PP_Resource</code> containing the new IME input event. + */ + [version=0.2] + PP_Resource Create([in] PP_Instance instance, + [in] PP_InputEvent_Type type, + [in] PP_TimeTicks time_stamp, + [in] PP_Var text, + [in] uint32_t segment_number, + [in] uint32_t[] segment_offsets, + [in] int32_t target_segment, + [in] uint32_t selection_start, + [in] uint32_t selection_end); + + /** * IsIMEInputEvent() determines if a resource is an IME event. * * @param[in] resource A <code>PP_Resource</code> corresponding to an event. diff --git a/ppapi/c/dev/ppb_ime_input_event_dev.h b/ppapi/c/dev/ppb_ime_input_event_dev.h index cb1af78..590a1d9 100644 --- a/ppapi/c/dev/ppb_ime_input_event_dev.h +++ b/ppapi/c/dev/ppb_ime_input_event_dev.h @@ -3,19 +3,23 @@ * found in the LICENSE file. */ -/* From dev/ppb_ime_input_event_dev.idl modified Wed Oct 5 14:06:02 2011. */ +/* From dev/ppb_ime_input_event_dev.idl modified Wed May 16 17:08:03 2012. */ #ifndef PPAPI_C_DEV_PPB_IME_INPUT_EVENT_DEV_H_ #define PPAPI_C_DEV_PPB_IME_INPUT_EVENT_DEV_H_ #include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_macros.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/pp_stdint.h" +#include "ppapi/c/pp_time.h" #include "ppapi/c/pp_var.h" +#include "ppapi/c/ppb_input_event.h" #define PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_1 "PPB_IMEInputEvent(Dev);0.1" -#define PPB_IME_INPUT_EVENT_DEV_INTERFACE PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_1 +#define PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_2 "PPB_IMEInputEvent(Dev);0.2" +#define PPB_IME_INPUT_EVENT_DEV_INTERFACE PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_2 /** * @file @@ -27,7 +31,52 @@ * @addtogroup Interfaces * @{ */ -struct PPB_IMEInputEvent_Dev_0_1 { +struct PPB_IMEInputEvent_Dev_0_2 { + /** + * Create() creates an IME input event with the given parameters. Normally + * you will get an IME event passed through the <code>HandleInputEvent</code> + * and will not need to create them, but some applications may want to create + * their own for internal use. + * + * @param[in] instance The instance for which this event occurred. + * + * @param[in] type A <code>PP_InputEvent_Type</code> identifying the type of + * input event. The type must be one of the IME event types. + * + * @param[in] time_stamp A <code>PP_TimeTicks</code> indicating the time + * when the event occurred. + * + * @param[in] text The string returned by <code>GetText</code>. + * + * @param[in] segment_number The number returned by + * <code>GetSegmentNumber</code>. + * + * @param[in] segment_offsets The array of numbers returned by + * <code>GetSegmentOffset</code>. If <code>segment_number</code> is zero, + * the number of elements of the array should be zero. If + * <code>segment_number</code> is non-zero, the length of the array must be + * <code>segment_number</code> + 1. + * + * @param[in] target_segment The number returned by + * <code>GetTargetSegment</code>. + * + * @param[in] selection_start The start index returned by + * <code>GetSelection</code>. + * + * @param[in] selection_end The end index returned by + * <code>GetSelection</code>. + * + * @return A <code>PP_Resource</code> containing the new IME input event. + */ + PP_Resource (*Create)(PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t segment_offsets[], + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end); /** * IsIMEInputEvent() determines if a resource is an IME event. * @@ -101,7 +150,16 @@ struct PPB_IMEInputEvent_Dev_0_1 { void (*GetSelection)(PP_Resource ime_event, uint32_t* start, uint32_t* end); }; -typedef struct PPB_IMEInputEvent_Dev_0_1 PPB_IMEInputEvent_Dev; +typedef struct PPB_IMEInputEvent_Dev_0_2 PPB_IMEInputEvent_Dev; + +struct PPB_IMEInputEvent_Dev_0_1 { + PP_Bool (*IsIMEInputEvent)(PP_Resource resource); + struct PP_Var (*GetText)(PP_Resource ime_event); + uint32_t (*GetSegmentNumber)(PP_Resource ime_event); + uint32_t (*GetSegmentOffset)(PP_Resource ime_event, uint32_t index); + int32_t (*GetTargetSegment)(PP_Resource ime_event); + void (*GetSelection)(PP_Resource ime_event, uint32_t* start, uint32_t* end); +}; /** * @} */ diff --git a/ppapi/cpp/dev/ime_input_event_dev.cc b/ppapi/cpp/dev/ime_input_event_dev.cc index 6f920e9..99fe66d 100644 --- a/ppapi/cpp/dev/ime_input_event_dev.cc +++ b/ppapi/cpp/dev/ime_input_event_dev.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -13,8 +13,12 @@ namespace pp { namespace { -template <> const char* interface_name<PPB_IMEInputEvent_Dev>() { - return PPB_IME_INPUT_EVENT_DEV_INTERFACE; +template <> const char* interface_name<PPB_IMEInputEvent_Dev_0_2>() { + return PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_2; +} + +template <> const char* interface_name<PPB_IMEInputEvent_Dev_0_1>() { + return PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_1; } } // namespace @@ -25,51 +29,101 @@ IMEInputEvent_Dev::IMEInputEvent_Dev() : InputEvent() { } IMEInputEvent_Dev::IMEInputEvent_Dev(const InputEvent& event) : InputEvent() { - // Type check the input event before setting it. - if (!has_interface<PPB_IMEInputEvent_Dev>()) - return; - if (get_interface<PPB_IMEInputEvent_Dev>()->IsIMEInputEvent( - event.pp_resource())) { + bool is_ime_event = false; + if (has_interface<PPB_IMEInputEvent_Dev_0_2>()) { + if (get_interface<PPB_IMEInputEvent_Dev_0_2>()->IsIMEInputEvent( + event.pp_resource())) { + is_ime_event = true; + } + } else if (has_interface<PPB_IMEInputEvent_Dev_0_1>()) { + if (get_interface<PPB_IMEInputEvent_Dev_0_1>()->IsIMEInputEvent( + event.pp_resource())) { + is_ime_event = true; + } + } + + if (is_ime_event) { Module::Get()->core()->AddRefResource(event.pp_resource()); PassRefFromConstructor(event.pp_resource()); } } +IMEInputEvent_Dev::IMEInputEvent_Dev( + const InstanceHandle& instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + Var text, + const std::vector<uint32_t>& segment_offsets, + int32_t target_segment, + const std::pair<uint32_t, uint32_t>& selection) : InputEvent() { + if (!has_interface<PPB_IMEInputEvent_Dev_0_2>()) + return; + uint32_t dummy = 0; + PassRefFromConstructor(get_interface<PPB_IMEInputEvent_Dev_0_2>()->Create( + instance.pp_instance(), type, time_stamp, text.pp_var(), + segment_offsets.empty() ? 0 : segment_offsets.size() - 1, + segment_offsets.empty() ? &dummy : &segment_offsets[0], + target_segment, selection.first, selection.second)); +} + + Var IMEInputEvent_Dev::GetText() const { - if (!has_interface<PPB_IMEInputEvent_Dev>()) - return Var(); - return Var(PASS_REF, - get_interface<PPB_IMEInputEvent_Dev>()->GetText(pp_resource())); + if (has_interface<PPB_IMEInputEvent_Dev_0_2>()) { + return Var(PASS_REF, + get_interface<PPB_IMEInputEvent_Dev_0_2>()->GetText( + pp_resource())); + } else if (has_interface<PPB_IMEInputEvent_Dev_0_1>()) { + return Var(PASS_REF, + get_interface<PPB_IMEInputEvent_Dev_0_1>()->GetText( + pp_resource())); + } + return Var(); } uint32_t IMEInputEvent_Dev::GetSegmentNumber() const { - if (!has_interface<PPB_IMEInputEvent_Dev>()) - return 0; - return get_interface<PPB_IMEInputEvent_Dev>()->GetSegmentNumber( - pp_resource()); + if (has_interface<PPB_IMEInputEvent_Dev_0_2>()) { + return get_interface<PPB_IMEInputEvent_Dev_0_2>()->GetSegmentNumber( + pp_resource()); + } else if (has_interface<PPB_IMEInputEvent_Dev_0_1>()) { + return get_interface<PPB_IMEInputEvent_Dev_0_1>()->GetSegmentNumber( + pp_resource()); + } + return 0; } uint32_t IMEInputEvent_Dev::GetSegmentOffset(uint32_t index) const { - if (!has_interface<PPB_IMEInputEvent_Dev>()) - return 0; - return get_interface<PPB_IMEInputEvent_Dev>()->GetSegmentOffset(pp_resource(), - index); + if (has_interface<PPB_IMEInputEvent_Dev_0_2>()) { + return get_interface<PPB_IMEInputEvent_Dev_0_2>()->GetSegmentOffset( + pp_resource(), index); + } else if (has_interface<PPB_IMEInputEvent_Dev_0_1>()) { + return get_interface<PPB_IMEInputEvent_Dev_0_1>()->GetSegmentOffset( + pp_resource(), index); + } + return 0; } int32_t IMEInputEvent_Dev::GetTargetSegment() const { - if (!has_interface<PPB_IMEInputEvent_Dev>()) - return 0; - return get_interface<PPB_IMEInputEvent_Dev>()->GetTargetSegment( - pp_resource()); + if (has_interface<PPB_IMEInputEvent_Dev_0_2>()) { + return get_interface<PPB_IMEInputEvent_Dev_0_2>()->GetTargetSegment( + pp_resource()); + } else if (has_interface<PPB_IMEInputEvent_Dev_0_1>()) { + return get_interface<PPB_IMEInputEvent_Dev_0_1>()->GetTargetSegment( + pp_resource()); + } + return 0; } std::pair<uint32_t, uint32_t> IMEInputEvent_Dev::GetSelection() const { std::pair<uint32_t, uint32_t> range(0, 0); - if (!has_interface<PPB_IMEInputEvent_Dev>()) - return range; - get_interface<PPB_IMEInputEvent_Dev>()->GetSelection(pp_resource(), - &range.first, - &range.second); + if (has_interface<PPB_IMEInputEvent_Dev_0_2>()) { + get_interface<PPB_IMEInputEvent_Dev_0_2>()->GetSelection(pp_resource(), + &range.first, + &range.second); + } else if (has_interface<PPB_IMEInputEvent_Dev_0_1>()) { + get_interface<PPB_IMEInputEvent_Dev_0_1>()->GetSelection(pp_resource(), + &range.first, + &range.second); + } return range; } diff --git a/ppapi/cpp/dev/ime_input_event_dev.h b/ppapi/cpp/dev/ime_input_event_dev.h index 17216a8..b9cb750 100644 --- a/ppapi/cpp/dev/ime_input_event_dev.h +++ b/ppapi/cpp/dev/ime_input_event_dev.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -6,6 +6,7 @@ #define PPAPI_CPP_DEV_IME_INPUT_EVENT_DEV_H_ #include <utility> +#include <vector> #include "ppapi/c/dev/ppb_ime_input_event_dev.h" #include "ppapi/cpp/input_event.h" @@ -29,6 +30,34 @@ class IMEInputEvent_Dev : public InputEvent { /// @param[in] event A generic input event. explicit IMEInputEvent_Dev(const InputEvent& event); + /// This constructor manually constructs an IME event from the provided + /// parameters. + /// + /// @param[in] instance The instance for which this event occurred. + /// + /// @param[in] type A <code>PP_InputEvent_Type</code> identifying the type of + /// input event. The type must be one of the ime event types. + /// + /// @param[in] time_stamp A <code>PP_TimeTicks</code> indicating the time + /// when the event occurred. + /// + /// @param[in] text The string returned by <code>GetText</code>. + /// + /// @param[in] segment_offsets The array of numbers returned by + /// <code>GetSegmentOffset</code>. + /// + /// @param[in] target_segment The number returned by + /// <code>GetTargetSegment</code>. + /// + /// @param[in] selection The range returned by <code>GetSelection</code>. + IMEInputEvent_Dev(const InstanceHandle& instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + Var text, + const std::vector<uint32_t>& segment_offsets, + int32_t target_segment, + const std::pair<uint32_t, uint32_t>& selection); + /// Returns the composition text as a UTF-8 string for the given IME event. /// /// @return A string var representing the composition text. For non-IME diff --git a/ppapi/ppapi_sources.gypi b/ppapi/ppapi_sources.gypi index 05cf661..6e6d617 100644 --- a/ppapi/ppapi_sources.gypi +++ b/ppapi/ppapi_sources.gypi @@ -335,6 +335,8 @@ 'tests/test_host_resolver_private.h', 'tests/test_image_data.cc', 'tests/test_image_data.h', + 'tests/test_ime_input_event.cc', + 'tests/test_ime_input_event.h', 'tests/test_input_event.cc', 'tests/test_input_event.h', 'tests/test_memory.cc', diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc index 7a4cc01..b5c254c 100644 --- a/ppapi/proxy/resource_creation_proxy.cc +++ b/ppapi/proxy/resource_creation_proxy.cc @@ -76,6 +76,21 @@ PP_Resource ResourceCreationProxy::CreateFileSystem( return PPB_FileSystem_Proxy::CreateProxyResource(instance, type); } +PP_Resource ResourceCreationProxy::CreateIMEInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) { + return PPB_InputEvent_Shared::CreateIMEInputEvent( + OBJECT_IS_PROXY, instance, type, time_stamp, text, segment_number, + segment_offsets, target_segment, selection_start, selection_end); +} + PP_Resource ResourceCreationProxy::CreateKeyboardInputEvent( PP_Instance instance, PP_InputEvent_Type type, @@ -83,25 +98,9 @@ PP_Resource ResourceCreationProxy::CreateKeyboardInputEvent( uint32_t modifiers, uint32_t key_code, struct PP_Var character_text) { - if (type != PP_INPUTEVENT_TYPE_RAWKEYDOWN && - type != PP_INPUTEVENT_TYPE_KEYDOWN && - type != PP_INPUTEVENT_TYPE_KEYUP && - type != PP_INPUTEVENT_TYPE_CHAR) - return 0; - InputEventData data; - data.event_type = type; - data.event_time_stamp = time_stamp; - data.event_modifiers = modifiers; - data.key_code = key_code; - if (character_text.type == PP_VARTYPE_STRING) { - StringVar* text_str = StringVar::FromPPVar(character_text); - if (!text_str) - return 0; - data.character_text = text_str->value(); - } - - return (new PPB_InputEvent_Shared(OBJECT_IS_PROXY, - instance, data))->GetReference(); + return PPB_InputEvent_Shared::CreateKeyboardInputEvent( + OBJECT_IS_PROXY, instance, type, time_stamp, modifiers, key_code, + character_text); } PP_Resource ResourceCreationProxy::CreateMouseInputEvent( @@ -113,24 +112,9 @@ PP_Resource ResourceCreationProxy::CreateMouseInputEvent( const PP_Point* mouse_position, int32_t click_count, const PP_Point* mouse_movement) { - if (type != PP_INPUTEVENT_TYPE_MOUSEDOWN && - type != PP_INPUTEVENT_TYPE_MOUSEUP && - type != PP_INPUTEVENT_TYPE_MOUSEMOVE && - type != PP_INPUTEVENT_TYPE_MOUSEENTER && - type != PP_INPUTEVENT_TYPE_MOUSELEAVE) - return 0; - - InputEventData data; - data.event_type = type; - data.event_time_stamp = time_stamp; - data.event_modifiers = modifiers; - data.mouse_button = mouse_button; - data.mouse_position = *mouse_position; - data.mouse_click_count = click_count; - data.mouse_movement = *mouse_movement; - - return (new PPB_InputEvent_Shared(OBJECT_IS_PROXY, - instance, data))->GetReference(); + return PPB_InputEvent_Shared::CreateMouseInputEvent( + OBJECT_IS_PROXY, instance, type, time_stamp, modifiers, + mouse_button, mouse_position, click_count, mouse_movement); } PP_Resource ResourceCreationProxy::CreateResourceArray( @@ -160,16 +144,9 @@ PP_Resource ResourceCreationProxy::CreateWheelInputEvent( const PP_FloatPoint* wheel_delta, const PP_FloatPoint* wheel_ticks, PP_Bool scroll_by_page) { - InputEventData data; - data.event_type = PP_INPUTEVENT_TYPE_WHEEL; - data.event_time_stamp = time_stamp; - data.event_modifiers = modifiers; - data.wheel_delta = *wheel_delta; - data.wheel_ticks = *wheel_ticks; - data.wheel_scroll_by_page = PP_ToBool(scroll_by_page); - - return (new PPB_InputEvent_Shared(OBJECT_IS_PROXY, - instance, data))->GetReference(); + return PPB_InputEvent_Shared::CreateWheelInputEvent( + OBJECT_IS_PROXY, instance, time_stamp, modifiers, + wheel_delta, wheel_ticks, scroll_by_page); } PP_Resource ResourceCreationProxy::CreateAudio( diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h index 2db304d..57af6f8 100644 --- a/ppapi/proxy/resource_creation_proxy.h +++ b/ppapi/proxy/resource_creation_proxy.h @@ -41,6 +41,15 @@ class ResourceCreationProxy : public InterfaceProxy, const char* path) OVERRIDE; virtual PP_Resource CreateFileSystem(PP_Instance instance, PP_FileSystemType type) OVERRIDE; + virtual PP_Resource CreateIMEInputEvent(PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) OVERRIDE; virtual PP_Resource CreateKeyboardInputEvent( PP_Instance instance, PP_InputEvent_Type type, diff --git a/ppapi/shared_impl/ppb_input_event_shared.cc b/ppapi/shared_impl/ppb_input_event_shared.cc index cdb0565..7b07de9 100644 --- a/ppapi/shared_impl/ppb_input_event_shared.cc +++ b/ppapi/shared_impl/ppb_input_event_shared.cc @@ -127,4 +127,122 @@ void PPB_InputEvent_Shared::GetIMESelection(uint32_t* start, uint32_t* end) { *end = data_.composition_selection_end; } +//static +PP_Resource PPB_InputEvent_Shared::CreateIMEInputEvent( + ResourceObjectType type, + PP_Instance instance, + PP_InputEvent_Type event_type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) { + if (event_type != PP_INPUTEVENT_TYPE_IME_COMPOSITION_START && + event_type != PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE && + event_type != PP_INPUTEVENT_TYPE_IME_COMPOSITION_END && + event_type != PP_INPUTEVENT_TYPE_IME_TEXT) + return 0; + + InputEventData data; + data.event_type = event_type; + data.event_time_stamp = time_stamp; + if (text.type == PP_VARTYPE_STRING) { + StringVar* text_str = StringVar::FromPPVar(text); + if (!text_str) + return 0; + data.character_text = text_str->value(); + } + data.composition_target_segment = target_segment; + if (segment_number != 0) { + data.composition_segment_offsets.assign( + &segment_offsets[0], &segment_offsets[segment_number + 1]); + } + data.composition_selection_start = selection_start; + data.composition_selection_end = selection_end; + + return (new PPB_InputEvent_Shared(type, instance, data))->GetReference(); +} + +//static +PP_Resource PPB_InputEvent_Shared::CreateKeyboardInputEvent( + ResourceObjectType type, + PP_Instance instance, + PP_InputEvent_Type event_type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + uint32_t key_code, + struct PP_Var character_text) { + if (event_type != PP_INPUTEVENT_TYPE_RAWKEYDOWN && + event_type != PP_INPUTEVENT_TYPE_KEYDOWN && + event_type != PP_INPUTEVENT_TYPE_KEYUP && + event_type != PP_INPUTEVENT_TYPE_CHAR) + return 0; + + InputEventData data; + data.event_type = event_type; + data.event_time_stamp = time_stamp; + data.event_modifiers = modifiers; + data.key_code = key_code; + if (character_text.type == PP_VARTYPE_STRING) { + StringVar* text_str = StringVar::FromPPVar(character_text); + if (!text_str) + return 0; + data.character_text = text_str->value(); + } + + return (new PPB_InputEvent_Shared(type, instance, data))->GetReference(); +} + +//static +PP_Resource PPB_InputEvent_Shared::CreateMouseInputEvent( + ResourceObjectType type, + PP_Instance instance, + PP_InputEvent_Type event_type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + PP_InputEvent_MouseButton mouse_button, + const PP_Point* mouse_position, + int32_t click_count, + const PP_Point* mouse_movement) { + if (event_type != PP_INPUTEVENT_TYPE_MOUSEDOWN && + event_type != PP_INPUTEVENT_TYPE_MOUSEUP && + event_type != PP_INPUTEVENT_TYPE_MOUSEMOVE && + event_type != PP_INPUTEVENT_TYPE_MOUSEENTER && + event_type != PP_INPUTEVENT_TYPE_MOUSELEAVE) + return 0; + + InputEventData data; + data.event_type = event_type; + data.event_time_stamp = time_stamp; + data.event_modifiers = modifiers; + data.mouse_button = mouse_button; + data.mouse_position = *mouse_position; + data.mouse_click_count = click_count; + data.mouse_movement = *mouse_movement; + + return (new PPB_InputEvent_Shared(type, instance, data))->GetReference(); +} + +//static +PP_Resource PPB_InputEvent_Shared::CreateWheelInputEvent( + ResourceObjectType type, + PP_Instance instance, + PP_TimeTicks time_stamp, + uint32_t modifiers, + const PP_FloatPoint* wheel_delta, + const PP_FloatPoint* wheel_ticks, + PP_Bool scroll_by_page) { + InputEventData data; + data.event_type = PP_INPUTEVENT_TYPE_WHEEL; + data.event_time_stamp = time_stamp; + data.event_modifiers = modifiers; + data.wheel_delta = *wheel_delta; + data.wheel_ticks = *wheel_ticks; + data.wheel_scroll_by_page = PP_ToBool(scroll_by_page); + + return (new PPB_InputEvent_Shared(type, instance, data))->GetReference(); +} + } // namespace ppapi diff --git a/ppapi/shared_impl/ppb_input_event_shared.h b/ppapi/shared_impl/ppb_input_event_shared.h index c62befa..74108b3 100644 --- a/ppapi/shared_impl/ppb_input_event_shared.h +++ b/ppapi/shared_impl/ppb_input_event_shared.h @@ -84,6 +84,42 @@ class PPAPI_SHARED_EXPORT PPB_InputEvent_Shared virtual int32_t GetIMETargetSegment() OVERRIDE; virtual void GetIMESelection(uint32_t* start, uint32_t* end) OVERRIDE; + // Implementations for event creation. + static PP_Resource CreateIMEInputEvent(ResourceObjectType type, + PP_Instance instance, + PP_InputEvent_Type event_type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end); + static PP_Resource CreateKeyboardInputEvent(ResourceObjectType type, + PP_Instance instance, + PP_InputEvent_Type event_type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + uint32_t key_code, + struct PP_Var character_text); + static PP_Resource CreateMouseInputEvent( + ResourceObjectType type, + PP_Instance instance, + PP_InputEvent_Type event_type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + PP_InputEvent_MouseButton mouse_button, + const PP_Point* mouse_position, + int32_t click_count, + const PP_Point* mouse_movement); + static PP_Resource CreateWheelInputEvent(ResourceObjectType type, + PP_Instance instance, + PP_TimeTicks time_stamp, + uint32_t modifiers, + const PP_FloatPoint* wheel_delta, + const PP_FloatPoint* wheel_ticks, + PP_Bool scroll_by_page); + private: InputEventData data_; diff --git a/ppapi/tests/test_ime_input_event.cc b/ppapi/tests/test_ime_input_event.cc new file mode 100644 index 0000000..804e17d7a --- /dev/null +++ b/ppapi/tests/test_ime_input_event.cc @@ -0,0 +1,425 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ppapi/tests/test_ime_input_event.h" + +#include "ppapi/c/dev/ppb_ime_input_event_dev.h" +#include "ppapi/c/dev/ppb_testing_dev.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/ppb_input_event.h" +#include "ppapi/cpp/dev/ime_input_event_dev.h" +#include "ppapi/cpp/input_event.h" +#include "ppapi/cpp/module.h" +#include "ppapi/tests/test_utils.h" +#include "ppapi/tests/testing_instance.h" + +REGISTER_TEST_CASE(ImeInputEvent); + +namespace { + +// Japanese Kanji letters meaning "a string" ('mo' 'ji' 'retsu' in Kanji) +const char* kCompositionChar[] = { + "\xE6\x96\x87", "\xE5\xAD\x97", "\xE5\x88\x97" +}; + +const char kCompositionText[] = "\xE6\x96\x87\xE5\xAD\x97\xE5\x88\x97"; + +#define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING" + +} // namespace + +TestImeInputEvent::TestImeInputEvent(TestingInstance* instance) + : TestCase(instance), + input_event_interface_(NULL), + keyboard_input_event_interface_(NULL), + ime_input_event_interface_(NULL), + received_unexpected_event_(true), + received_finish_message_(false) { +} + +TestImeInputEvent::~TestImeInputEvent() { + // Remove the special listener that only responds to a + // FINISHED_WAITING_MESSAGE string. See Init for where it gets added. + std::string js_code; + js_code = "var plugin = document.getElementById('plugin');" + "plugin.removeEventListener('message'," + " plugin.wait_for_messages_handler);" + "delete plugin.wait_for_messages_handler;"; + instance_->EvalScript(js_code); +} + +void TestImeInputEvent::RunTests(const std::string& filter) { + RUN_TEST(ImeCommit, filter); + RUN_TEST(ImeCancel, filter); + RUN_TEST(ImeUnawareCommit, filter); + RUN_TEST(ImeUnawareCancel, filter); +} + +bool TestImeInputEvent::Init() { + input_event_interface_ = static_cast<const PPB_InputEvent*>( + pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE)); + keyboard_input_event_interface_ = + static_cast<const PPB_KeyboardInputEvent*>( + pp::Module::Get()->GetBrowserInterface( + PPB_KEYBOARD_INPUT_EVENT_INTERFACE)); + ime_input_event_interface_ = static_cast<const PPB_IMEInputEvent_Dev*>( + pp::Module::Get()->GetBrowserInterface( + PPB_IME_INPUT_EVENT_DEV_INTERFACE)); + + bool success = + input_event_interface_ && + keyboard_input_event_interface_ && + ime_input_event_interface_ && + CheckTestingInterface(); + + // Set up a listener for our message that signals that all input events have + // been received. + // Note the following code is dependent on some features of test_case.html. + // E.g., it is assumed that the DOM element where the plugin is embedded has + // an id of 'plugin', and there is a function 'IsTestingMessage' that allows + // us to ignore the messages that are intended for use by the testing + // framework itself. + std::string js_code = + "var plugin = document.getElementById('plugin');" + "var wait_for_messages_handler = function(message_event) {" + " if (!IsTestingMessage(message_event.data) &&" + " message_event.data === '" FINISHED_WAITING_MESSAGE "') {" + " plugin.postMessage('" FINISHED_WAITING_MESSAGE "');" + " }" + "};" + "plugin.addEventListener('message', wait_for_messages_handler);" + // Stash it on the plugin so we can remove it in the destructor. + "plugin.wait_for_messages_handler = wait_for_messages_handler;"; + instance_->EvalScript(js_code); + + return success; +} + +bool TestImeInputEvent::HandleInputEvent(const pp::InputEvent& input_event) { + // Check whether the IME related events comes in the expected order. + switch (input_event.GetType()) { + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: + case PP_INPUTEVENT_TYPE_IME_TEXT: + case PP_INPUTEVENT_TYPE_CHAR: + if (expected_events_.empty()) { + received_unexpected_event_ = true; + } else { + received_unexpected_event_ = + !AreEquivalentEvents(input_event.pp_resource(), + expected_events_.front().pp_resource()); + expected_events_.erase(expected_events_.begin()); + } + break; + + default: + // Don't care for any other input event types for this test. + break; + } + + // Handle all input events. + return true; +} + +void TestImeInputEvent::HandleMessage(const pp::Var& message_data) { + if (message_data.is_string() && + (message_data.AsString() == FINISHED_WAITING_MESSAGE)) { + testing_interface_->QuitMessageLoop(instance_->pp_instance()); + received_finish_message_ = true; + } +} + +void TestImeInputEvent::DidChangeView(const pp::View& view) { + view_rect_ = view.GetRect(); +} + +pp::InputEvent TestImeInputEvent::CreateImeCompositionStartEvent() { + return pp::IMEInputEvent_Dev( + instance_, + PP_INPUTEVENT_TYPE_IME_COMPOSITION_START, + 100, // time_stamp + pp::Var(""), + std::vector<uint32_t>(), + -1, // target_segment + std::make_pair(0U, 0U) // selection + ); +} + +pp::InputEvent TestImeInputEvent::CreateImeCompositionUpdateEvent( + const std::string& text, + const std::vector<uint32_t>& segments, + int32_t target_segment, + const std::pair<uint32_t, uint32_t>& selection) { + return pp::IMEInputEvent_Dev( + instance_, + PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE, + 100, // time_stamp + text, + segments, + target_segment, + selection + ); +} + +pp::InputEvent TestImeInputEvent::CreateImeCompositionEndEvent( + const std::string& text) { + return pp::IMEInputEvent_Dev( + instance_, + PP_INPUTEVENT_TYPE_IME_COMPOSITION_END, + 100, // time_stamp + pp::Var(text), + std::vector<uint32_t>(), + -1, // target_segment + std::make_pair(0U, 0U) // selection + ); +} + +pp::InputEvent TestImeInputEvent::CreateImeTextEvent(const std::string& text) { + return pp::IMEInputEvent_Dev( + instance_, + PP_INPUTEVENT_TYPE_IME_TEXT, + 100, // time_stamp + pp::Var(text), + std::vector<uint32_t>(), + -1, // target_segment + std::make_pair(0U, 0U) // selection + ); +} + +pp::InputEvent TestImeInputEvent::CreateCharEvent(const std::string& text) { + return pp::KeyboardInputEvent( + instance_, + PP_INPUTEVENT_TYPE_CHAR, + 100, // time_stamp + 0, // modifiers + 0, // keycode + pp::Var(text)); +} + +void TestImeInputEvent::GetFocusBySimulatingMouseClick() { + // For receiving IME events, the plugin DOM node needs to be focused. + // The following code is for achieving that by simulating a mouse click event. + input_event_interface_->RequestInputEvents(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_MOUSE); + SimulateInputEvent(pp::MouseInputEvent( + instance_, + PP_INPUTEVENT_TYPE_MOUSEDOWN, + 100, // time_stamp + 0, // modifiers + PP_INPUTEVENT_MOUSEBUTTON_LEFT, + pp::Point( + view_rect_.x() + view_rect_.width() / 2, + view_rect_.y() + view_rect_.height() / 2), + 1, // click count + pp::Point())); // movement +} + +// Simulates the input event and calls PostMessage to let us know when +// we have received all resulting events from the browser. +bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent& input_event) { + received_unexpected_event_ = false; + received_finish_message_ = false; + testing_interface_->SimulateInputEvent(instance_->pp_instance(), + input_event.pp_resource()); + instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE)); + testing_interface_->RunMessageLoop(instance_->pp_instance()); + return received_finish_message_ && !received_unexpected_event_; +} + +bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received, + PP_Resource expected) { + if (!input_event_interface_->IsInputEvent(received) || + !input_event_interface_->IsInputEvent(expected)) { + return false; + } + + // Test common fields, except modifiers and time stamp, which may be changed + // by the browser. + int32_t received_type = input_event_interface_->GetType(received); + int32_t expected_type = input_event_interface_->GetType(expected); + if (received_type != expected_type) + return false; + + // Test event type-specific fields. + switch (received_type) { + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: + // COMPOSITION_START does not convey further information. + break; + + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: + case PP_INPUTEVENT_TYPE_IME_TEXT: + // For COMPOSITION_END and TEXT, GetText() has meaning. + return pp::Var(pp::PASS_REF, + ime_input_event_interface_->GetText(received)) == + pp::Var(pp::PASS_REF, + ime_input_event_interface_->GetText(expected)); + + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: + // For COMPOSITION_UPDATE, all fields must be checked. + { + uint32_t received_segment_number = + ime_input_event_interface_->GetSegmentNumber(received); + uint32_t expected_segment_number = + ime_input_event_interface_->GetSegmentNumber(expected); + if (received_segment_number != expected_segment_number) + return false; + + // The "<=" is not a bug. i-th segment is represented as the pair of + // i-th and (i+1)-th offsets in Pepper IME API. + for (uint32_t i = 0; i <= received_segment_number; ++i) { + if (ime_input_event_interface_->GetSegmentOffset(received, i) != + ime_input_event_interface_->GetSegmentOffset(expected, i)) + return false; + } + + uint32_t received_selection_start = 0; + uint32_t received_selection_end = 0; + uint32_t expected_selection_start = 0; + uint32_t expected_selection_end = 0; + ime_input_event_interface_->GetSelection( + received, &received_selection_start, &received_selection_end); + ime_input_event_interface_->GetSelection( + expected, &expected_selection_start, &expected_selection_end); + if (received_selection_start != expected_selection_start || + received_selection_end != expected_selection_end) { + return true; + } + + return pp::Var(pp::PASS_REF, + ime_input_event_interface_->GetText(received)) == + pp::Var(pp::PASS_REF, + ime_input_event_interface_->GetText(expected)) && + ime_input_event_interface_->GetTargetSegment(received) == + ime_input_event_interface_->GetTargetSegment(expected); + } + + case PP_INPUTEVENT_TYPE_CHAR: + return + keyboard_input_event_interface_->GetKeyCode(received) == + keyboard_input_event_interface_->GetKeyCode(expected) && + pp::Var(pp::PASS_REF, + keyboard_input_event_interface_->GetCharacterText(received)) == + pp::Var(pp::PASS_REF, + keyboard_input_event_interface_->GetCharacterText(expected)); + + default: + break; + } + return true; +} + +std::string TestImeInputEvent::TestImeCommit() { + GetFocusBySimulatingMouseClick(); + + input_event_interface_->RequestInputEvents(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_KEYBOARD | + PP_INPUTEVENT_CLASS_IME); + + std::vector<uint32_t> segments; + segments.push_back(0U); + segments.push_back(3U); + segments.push_back(6U); + segments.push_back(9U); + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( + kCompositionText, segments, 1, std::make_pair(3U, 6U)); + + expected_events_.clear(); + expected_events_.push_back(CreateImeCompositionStartEvent()); + expected_events_.push_back(update_event); + expected_events_.push_back(CreateImeCompositionEndEvent(kCompositionText)); + expected_events_.push_back(CreateImeTextEvent(kCompositionText)); + + // Simulate the case when IME successfully committed some text. + ASSERT_TRUE(SimulateInputEvent(update_event)); + ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText))); + + ASSERT_TRUE(expected_events_.empty()); + PASS(); +} + +std::string TestImeInputEvent::TestImeCancel() { + GetFocusBySimulatingMouseClick(); + + input_event_interface_->RequestInputEvents(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_KEYBOARD | + PP_INPUTEVENT_CLASS_IME); + + std::vector<uint32_t> segments; + segments.push_back(0U); + segments.push_back(3U); + segments.push_back(6U); + segments.push_back(9U); + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( + kCompositionText, segments, 1, std::make_pair(3U, 6U)); + + expected_events_.clear(); + expected_events_.push_back(CreateImeCompositionStartEvent()); + expected_events_.push_back(update_event); + expected_events_.push_back(CreateImeCompositionEndEvent("")); + + // Simulate the case when IME canceled composition. + ASSERT_TRUE(SimulateInputEvent(update_event)); + ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(""))); + + ASSERT_TRUE(expected_events_.empty()); + PASS(); +} + +std::string TestImeInputEvent::TestImeUnawareCommit() { + GetFocusBySimulatingMouseClick(); + + input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_IME); + input_event_interface_->RequestInputEvents(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_KEYBOARD); + + std::vector<uint32_t> segments; + segments.push_back(0U); + segments.push_back(3U); + segments.push_back(6U); + segments.push_back(9U); + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( + kCompositionText, segments, 1, std::make_pair(3U, 6U)); + + expected_events_.clear(); + expected_events_.push_back(CreateCharEvent(kCompositionChar[0])); + expected_events_.push_back(CreateCharEvent(kCompositionChar[1])); + expected_events_.push_back(CreateCharEvent(kCompositionChar[2])); + + // Test for IME-unaware plugins. Commit event is translated to char events. + ASSERT_TRUE(SimulateInputEvent(update_event)); + ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText))); + + ASSERT_TRUE(expected_events_.empty()); + PASS(); +} + + +std::string TestImeInputEvent::TestImeUnawareCancel() { + GetFocusBySimulatingMouseClick(); + + input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_IME); + input_event_interface_->RequestInputEvents(instance_->pp_instance(), + PP_INPUTEVENT_CLASS_KEYBOARD); + + std::vector<uint32_t> segments; + segments.push_back(0U); + segments.push_back(3U); + segments.push_back(6U); + segments.push_back(9U); + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( + kCompositionText, segments, 1, std::make_pair(3U, 6U)); + + expected_events_.clear(); + + // Test for IME-unaware plugins. Cancel won't issue any events. + ASSERT_TRUE(SimulateInputEvent(update_event)); + ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(""))); + + ASSERT_TRUE(expected_events_.empty()); + PASS(); +} + diff --git a/ppapi/tests/test_ime_input_event.h b/ppapi/tests/test_ime_input_event.h new file mode 100644 index 0000000..483167c --- /dev/null +++ b/ppapi/tests/test_ime_input_event.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 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 PPAPI_TESTS_TEST_IME_INPUT_EVENT_H_ +#define PPAPI_TESTS_TEST_IME_INPUT_EVENT_H_ + +#include <string> +#include <utility> +#include <vector> + +#include "ppapi/c/dev/ppb_ime_input_event_dev.h" +#include "ppapi/c/ppb_input_event.h" +#include "ppapi/cpp/input_event.h" +#include "ppapi/tests/test_case.h" + +class TestImeInputEvent : public TestCase { + public: + explicit TestImeInputEvent(TestingInstance* instance); + ~TestImeInputEvent(); + + // TestCase implementation. + virtual void RunTests(const std::string& test_filter); + virtual bool Init(); + virtual bool HandleInputEvent(const pp::InputEvent& input_event); + virtual void HandleMessage(const pp::Var& message_data); + virtual void DidChangeView(const pp::View& view); + + private: + pp::InputEvent CreateImeCompositionStartEvent(); + pp::InputEvent CreateImeCompositionUpdateEvent( + const std::string& text, + const std::vector<uint32_t>& segments, + int32_t target_segment, + const std::pair<uint32_t, uint32_t>& selection); + pp::InputEvent CreateImeCompositionEndEvent(const std::string& text); + pp::InputEvent CreateImeTextEvent(const std::string& text); + pp::InputEvent CreateCharEvent(const std::string& text); + + void GetFocusBySimulatingMouseClick(); + bool SimulateInputEvent(const pp::InputEvent& input_event); + bool AreEquivalentEvents(PP_Resource first, PP_Resource second); + + // The test cases. + std::string TestImeCommit(); + std::string TestImeCancel(); + std::string TestImeUnawareCommit(); + std::string TestImeUnawareCancel(); + + const PPB_InputEvent* input_event_interface_; + const PPB_KeyboardInputEvent* keyboard_input_event_interface_; + const PPB_IMEInputEvent_Dev* ime_input_event_interface_; + + pp::Rect view_rect_; + bool received_unexpected_event_; + bool received_finish_message_; + std::vector<pp::InputEvent> expected_events_; +}; + +#endif // PPAPI_TESTS_TEST_IME_INPUT_EVENT_H_ diff --git a/ppapi/thunk/interfaces_ppb_public_dev.h b/ppapi/thunk/interfaces_ppb_public_dev.h index 3b79fe4..692b4f3 100644 --- a/ppapi/thunk/interfaces_ppb_public_dev.h +++ b/ppapi/thunk/interfaces_ppb_public_dev.h @@ -26,6 +26,8 @@ PROXIED_IFACE(PPB_AudioInput, PPB_AUDIO_INPUT_DEV_INTERFACE_0_2, PPB_AudioInput_Dev_0_2) PROXIED_IFACE(NoAPIName, PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_1, PPB_IMEInputEvent_Dev_0_1) +PROXIED_IFACE(NoAPIName, PPB_IME_INPUT_EVENT_DEV_INTERFACE_0_2, + PPB_IMEInputEvent_Dev_0_2) PROXIED_IFACE(PPB_Buffer, PPB_BUFFER_DEV_INTERFACE_0_4, PPB_Buffer_Dev_0_4) PROXIED_IFACE(PPB_Graphics3D, PPB_GLES_CHROMIUM_TEXTURE_MAPPING_DEV_INTERFACE_0_1, diff --git a/ppapi/thunk/ppb_input_event_thunk.cc b/ppapi/thunk/ppb_input_event_thunk.cc index 8f3f65c..47f9e51 100644 --- a/ppapi/thunk/ppb_input_event_thunk.cc +++ b/ppapi/thunk/ppb_input_event_thunk.cc @@ -293,6 +293,26 @@ const PPB_KeyboardInputEvent_Dev g_ppb_keyboard_input_event_dev_thunk = { // Composition ----------------------------------------------------------------- +PP_Resource CreateIMEInputEvent(PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + PP_Var text, + uint32_t segment_number, + const uint32_t segment_offsets[], + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) { + EnterResourceCreation enter(instance); + if (enter.failed()) + return 0; + return enter.functions()->CreateIMEInputEvent(instance, type, time_stamp, + text, segment_number, + segment_offsets, + target_segment, + selection_start, + selection_end); +} + PP_Bool IsIMEInputEvent(PP_Resource resource) { if (!IsInputEvent(resource)) return PP_FALSE; // Prevent warning log in GetType. @@ -340,7 +360,17 @@ void GetIMESelection(PP_Resource ime_event, uint32_t* start, uint32_t* end) { enter.object()->GetIMESelection(start, end); } -const PPB_IMEInputEvent_Dev g_ppb_ime_input_event_thunk = { +const PPB_IMEInputEvent_Dev_0_1 g_ppb_ime_input_event_0_1_thunk = { + &IsIMEInputEvent, + &GetIMEText, + &GetIMESegmentNumber, + &GetIMESegmentOffset, + &GetIMETargetSegment, + &GetIMESelection +}; + +const PPB_IMEInputEvent_Dev_0_2 g_ppb_ime_input_event_0_2_thunk = { + &CreateIMEInputEvent, &IsIMEInputEvent, &GetIMEText, &GetIMESegmentNumber, @@ -377,7 +407,11 @@ const PPB_WheelInputEvent_1_0* GetPPB_WheelInputEvent_1_0_Thunk() { } const PPB_IMEInputEvent_Dev_0_1* GetPPB_IMEInputEvent_Dev_0_1_Thunk() { - return &g_ppb_ime_input_event_thunk; + return &g_ppb_ime_input_event_0_1_thunk; +} + +const PPB_IMEInputEvent_Dev_0_2* GetPPB_IMEInputEvent_Dev_0_2_Thunk() { + return &g_ppb_ime_input_event_0_2_thunk; } } // namespace thunk diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h index ee7c5c2..2fd9d02 100644 --- a/ppapi/thunk/resource_creation_api.h +++ b/ppapi/thunk/resource_creation_api.h @@ -47,6 +47,15 @@ class ResourceCreationAPI { const char* path) = 0; virtual PP_Resource CreateFileSystem(PP_Instance instance, PP_FileSystemType type) = 0; + virtual PP_Resource CreateIMEInputEvent(PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) = 0; virtual PP_Resource CreateKeyboardInputEvent( PP_Instance instance, PP_InputEvent_Type type, diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.cc b/webkit/plugins/ppapi/mock_plugin_delegate.cc index 971754c..7c7329f 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.cc +++ b/webkit/plugins/ppapi/mock_plugin_delegate.cc @@ -37,6 +37,16 @@ void MockPluginDelegate::PluginRequestedCancelComposition( void MockPluginDelegate::PluginSelectionChanged(PluginInstance* instance) { } +void MockPluginDelegate::SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) { +} + +void MockPluginDelegate::SimulateImeConfirmComposition(const string16& text) { +} + void MockPluginDelegate::PluginCrashed(PluginInstance* instance) { } diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.h b/webkit/plugins/ppapi/mock_plugin_delegate.h index 91c62e0..3136847 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.h +++ b/webkit/plugins/ppapi/mock_plugin_delegate.h @@ -24,6 +24,12 @@ class MockPluginDelegate : public PluginDelegate { virtual void PluginCaretPositionChanged(PluginInstance* instance); virtual void PluginRequestedCancelComposition(PluginInstance* instance); virtual void PluginSelectionChanged(PluginInstance* instance); + virtual void SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end); + virtual void SimulateImeConfirmComposition(const string16& text); virtual void PluginCrashed(PluginInstance* instance); virtual void InstanceCreated(PluginInstance* instance); virtual void InstanceDeleted(PluginInstance* instance); diff --git a/webkit/plugins/ppapi/plugin_delegate.h b/webkit/plugins/ppapi/plugin_delegate.h index 304cb09..9229143 100644 --- a/webkit/plugins/ppapi/plugin_delegate.h +++ b/webkit/plugins/ppapi/plugin_delegate.h @@ -70,6 +70,7 @@ namespace WebKit { class WebFileChooserCompletion; class WebGamepads; class WebPlugin; +struct WebCompositionUnderline; struct WebCursorInfo; struct WebFileChooserParams; } @@ -316,6 +317,13 @@ class PluginDelegate { // Notification that the text selection in the given plugin is changed. virtual void PluginSelectionChanged( webkit::ppapi::PluginInstance* instance) = 0; + // Requests simulating IME events for testing purpose. + virtual void SimulateImeSetComposition( + const string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) = 0; + virtual void SimulateImeConfirmComposition(const string16& text) = 0; // Notification that the given plugin has crashed. When a plugin crashes, all // instances associated with that plugin will notify that they've crashed via diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.cc b/webkit/plugins/ppapi/ppapi_plugin_instance.cc index 390f8dc..429dd03 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc @@ -1611,6 +1611,10 @@ void PluginInstance::SimulateInputEvent(const InputEventData& input_event) { return; } + bool handled = SimulateIMEEvent(input_event); + if (handled) + return; + std::vector<linked_ptr<WebInputEvent> > events = CreateSimulatedWebInputEvents( input_event, @@ -1622,6 +1626,52 @@ void PluginInstance::SimulateInputEvent(const InputEventData& input_event) { } } +bool PluginInstance::SimulateIMEEvent(const InputEventData& input_event) { + switch (input_event.event_type) { + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: + SimulateImeSetCompositionEvent(input_event); + break; + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: + DCHECK(input_event.character_text.empty()); + SimulateImeSetCompositionEvent(input_event); + break; + case PP_INPUTEVENT_TYPE_IME_TEXT: + delegate()->SimulateImeConfirmComposition( + UTF8ToUTF16(input_event.character_text)); + break; + default: + return false; + } + return true; +} + +void PluginInstance::SimulateImeSetCompositionEvent( + const InputEventData& input_event) { + std::vector<size_t> offsets; + offsets.push_back(input_event.composition_selection_start); + offsets.push_back(input_event.composition_selection_end); + offsets.insert(offsets.end(), + input_event.composition_segment_offsets.begin(), + input_event.composition_segment_offsets.end()); + + string16 utf16_text = + UTF8ToUTF16AndAdjustOffsets(input_event.character_text, &offsets); + + std::vector<WebKit::WebCompositionUnderline> underlines; + for (size_t i = 2; i + 1 < offsets.size(); ++i) { + WebKit::WebCompositionUnderline underline; + underline.startOffset = offsets[i]; + underline.endOffset = offsets[i + 1]; + if (input_event.composition_target_segment == static_cast<int32_t>(i - 2)) + underline.thick = true; + underlines.push_back(underline); + } + + delegate()->SimulateImeSetComposition( + utf16_text, underlines, offsets[0], offsets[1]); +} + void PluginInstance::ClosePendingUserGesture(PP_Instance instance, PP_TimeTicks timestamp) { // Close the pending user gesture if the plugin had a chance to respond. diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.h b/webkit/plugins/ppapi/ppapi_plugin_instance.h index 63b742f..ffd184b 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.h +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.h @@ -326,6 +326,12 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : // which sends it back up to the plugin as if it came from the user. void SimulateInputEvent(const ::ppapi::InputEventData& input_event); + // Simulates an IME event at the level of RenderView which sends it back up to + // the plugin as if it came from the user. + bool SimulateIMEEvent(const ::ppapi::InputEventData& input_event); + void SimulateImeSetCompositionEvent( + const ::ppapi::InputEventData& input_event); + // PPB_Instance_API implementation. virtual PP_Bool BindGraphics(PP_Instance instance, PP_Resource device) OVERRIDE; diff --git a/webkit/plugins/ppapi/resource_creation_impl.cc b/webkit/plugins/ppapi/resource_creation_impl.cc index 17ef13b..077e247 100644 --- a/webkit/plugins/ppapi/resource_creation_impl.cc +++ b/webkit/plugins/ppapi/resource_creation_impl.cc @@ -182,6 +182,21 @@ PP_Resource ResourceCreationImpl::CreateImageData(PP_Instance instance, return PPB_ImageData_Impl::Create(instance, format, size, init_to_zero); } +PP_Resource ResourceCreationImpl::CreateIMEInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) { + return PPB_InputEvent_Shared::CreateIMEInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, text, segment_number, + segment_offsets, target_segment, selection_start, selection_end); +} + PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent( PP_Instance instance, PP_InputEvent_Type type, @@ -189,26 +204,9 @@ PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent( uint32_t modifiers, uint32_t key_code, struct PP_Var character_text) { - if (type != PP_INPUTEVENT_TYPE_RAWKEYDOWN && - type != PP_INPUTEVENT_TYPE_KEYDOWN && - type != PP_INPUTEVENT_TYPE_KEYUP && - type != PP_INPUTEVENT_TYPE_CHAR) - return 0; - - InputEventData data; - data.event_type = type; - data.event_time_stamp = time_stamp; - data.event_modifiers = modifiers; - data.key_code = key_code; - if (character_text.type == PP_VARTYPE_STRING) { - StringVar* string_var = StringVar::FromPPVar(character_text); - if (!string_var) - return 0; - data.character_text = string_var->value(); - } - - return (new PPB_InputEvent_Shared(::ppapi::OBJECT_IS_IMPL, - instance, data))->GetReference(); + return PPB_InputEvent_Shared::CreateKeyboardInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, modifiers, key_code, + character_text); } PP_Resource ResourceCreationImpl::CreateMouseInputEvent( @@ -220,24 +218,9 @@ PP_Resource ResourceCreationImpl::CreateMouseInputEvent( const PP_Point* mouse_position, int32_t click_count, const PP_Point* mouse_movement) { - if (type != PP_INPUTEVENT_TYPE_MOUSEDOWN && - type != PP_INPUTEVENT_TYPE_MOUSEUP && - type != PP_INPUTEVENT_TYPE_MOUSEMOVE && - type != PP_INPUTEVENT_TYPE_MOUSEENTER && - type != PP_INPUTEVENT_TYPE_MOUSELEAVE) - return 0; - - InputEventData data; - data.event_type = type; - data.event_time_stamp = time_stamp; - data.event_modifiers = modifiers; - data.mouse_button = mouse_button; - data.mouse_position = *mouse_position; - data.mouse_click_count = click_count; - data.mouse_movement = *mouse_movement; - - return (new PPB_InputEvent_Shared(::ppapi::OBJECT_IS_IMPL, - instance, data))->GetReference(); + return PPB_InputEvent_Shared::CreateMouseInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, modifiers, + mouse_button, mouse_position, click_count, mouse_movement); } PP_Resource ResourceCreationImpl::CreateNetworkMonitor( @@ -330,16 +313,9 @@ PP_Resource ResourceCreationImpl::CreateWheelInputEvent( const PP_FloatPoint* wheel_delta, const PP_FloatPoint* wheel_ticks, PP_Bool scroll_by_page) { - InputEventData data; - data.event_type = PP_INPUTEVENT_TYPE_WHEEL; - data.event_time_stamp = time_stamp; - data.event_modifiers = modifiers; - data.wheel_delta = *wheel_delta; - data.wheel_ticks = *wheel_ticks; - data.wheel_scroll_by_page = PP_ToBool(scroll_by_page); - - return (new PPB_InputEvent_Shared(::ppapi::OBJECT_IS_IMPL, - instance, data))->GetReference(); + return PPB_InputEvent_Shared::CreateWheelInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, time_stamp, modifiers, + wheel_delta, wheel_ticks, scroll_by_page); } PP_Resource ResourceCreationImpl::CreateX509CertificatePrivate( diff --git a/webkit/plugins/ppapi/resource_creation_impl.h b/webkit/plugins/ppapi/resource_creation_impl.h index 44dae8e..ca8bb54 100644 --- a/webkit/plugins/ppapi/resource_creation_impl.h +++ b/webkit/plugins/ppapi/resource_creation_impl.h @@ -67,6 +67,15 @@ class ResourceCreationImpl : public ::ppapi::thunk::ResourceCreationAPI { PP_ImageDataFormat format, const PP_Size& size, PP_Bool init_to_zero) OVERRIDE; + virtual PP_Resource CreateIMEInputEvent(PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) OVERRIDE; virtual PP_Resource CreateKeyboardInputEvent( PP_Instance instance, PP_InputEvent_Type type, |