summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/renderer/pepper_plugin_delegate_impl.cc106
-rw-r--r--content/renderer/pepper_plugin_delegate_impl.h29
-rw-r--r--content/renderer/render_view_impl.cc69
-rw-r--r--content/renderer/render_view_impl.h7
-rw-r--r--content/renderer/render_widget.cc12
-rw-r--r--content/renderer/render_widget.h1
-rw-r--r--ppapi/examples/ime/ime.cc609
-rw-r--r--ppapi/examples/ime/ime.html31
-rw-r--r--ppapi/ppapi_tests.gypi10
-rw-r--r--webkit/glue/webkit_glue.gypi1
-rw-r--r--webkit/plugins/ppapi/DEPS1
-rw-r--r--webkit/plugins/ppapi/mock_plugin_delegate.cc10
-rw-r--r--webkit/plugins/ppapi/mock_plugin_delegate.h4
-rw-r--r--webkit/plugins/ppapi/plugin_delegate.h9
-rw-r--r--webkit/plugins/ppapi/ppapi_plugin_instance.cc164
-rw-r--r--webkit/plugins/ppapi/ppapi_plugin_instance.h46
-rw-r--r--webkit/plugins/ppapi/ppb_text_input_impl.cc36
17 files changed, 1069 insertions, 76 deletions
diff --git a/content/renderer/pepper_plugin_delegate_impl.cc b/content/renderer/pepper_plugin_delegate_impl.cc
index 35fc894..5966fdb 100644
--- a/content/renderer/pepper_plugin_delegate_impl.cc
+++ b/content/renderer/pepper_plugin_delegate_impl.cc
@@ -665,7 +665,7 @@ PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderViewImpl* render_view)
: render_view_(render_view),
has_saved_context_menu_action_(false),
saved_context_menu_action_(0),
- is_pepper_plugin_focused_(false),
+ focused_plugin_(NULL),
mouse_lock_owner_(NULL),
mouse_locked_(false),
pending_lock_request_(false),
@@ -849,12 +849,110 @@ PepperPluginDelegateImpl::GetBitmapForOptimizedPluginPaint(
return NULL;
}
-void PepperPluginDelegateImpl::PluginFocusChanged(bool focused) {
- is_pepper_plugin_focused_ = focused;
+void PepperPluginDelegateImpl::PluginFocusChanged(
+ webkit::ppapi::PluginInstance* instance,
+ bool focused) {
+ if (focused)
+ focused_plugin_ = instance;
+ else if (focused_plugin_ == instance)
+ focused_plugin_ = NULL;
if (render_view_)
render_view_->PpapiPluginFocusChanged();
}
+void PepperPluginDelegateImpl::PluginTextInputTypeChanged(
+ webkit::ppapi::PluginInstance* instance) {
+ if (focused_plugin_ == instance && render_view_)
+ render_view_->PpapiPluginTextInputTypeChanged();
+}
+
+void PepperPluginDelegateImpl::PluginRequestedCancelComposition(
+ webkit::ppapi::PluginInstance* instance) {
+ if (focused_plugin_ == instance && render_view_)
+ render_view_->PpapiPluginCancelComposition();
+}
+
+void PepperPluginDelegateImpl::OnImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end) {
+ if (!IsPluginAcceptingCompositionEvents()) {
+ composition_text_ = text;
+ } else {
+ // TODO(kinaba) currently all composition events are sent directly to
+ // plugins. Use DOM event mechanism after WebKit is made aware about
+ // plugins that support composition.
+ // The code below mimics the behavior of WebCore::Editor::setComposition.
+
+ // Empty -> nonempty: composition started.
+ if (composition_text_.empty() && !text.empty())
+ focused_plugin_->HandleCompositionStart(string16());
+ // Nonempty -> empty: composition canceled.
+ if (!composition_text_.empty() && text.empty())
+ focused_plugin_->HandleCompositionEnd(string16());
+ composition_text_ = text;
+ // Nonempty: composition is ongoing.
+ if (!composition_text_.empty()) {
+ focused_plugin_->HandleCompositionUpdate(composition_text_, underlines,
+ selection_start, selection_end);
+ }
+ }
+}
+
+void PepperPluginDelegateImpl::OnImeConfirmComposition(const string16& text) {
+ // Here, text.empty() has a special meaning. It means to commit the last
+ // update of composition text (see RenderWidgetHost::ImeConfirmComposition()).
+ const string16& last_text = text.empty() ? composition_text_ : text;
+
+ // last_text is empty only when both text and composition_text_ is. Ignore it.
+ if (last_text.empty())
+ return;
+
+ if (!IsPluginAcceptingCompositionEvents()) {
+ for (size_t i = 0; i < text.size(); ++i) {
+ WebKit::WebKeyboardEvent char_event;
+ char_event.type = WebKit::WebInputEvent::Char;
+ char_event.timeStampSeconds = base::Time::Now().ToDoubleT();
+ char_event.modifiers = 0;
+ char_event.windowsKeyCode = last_text[i];
+ char_event.nativeKeyCode = last_text[i];
+ char_event.text[0] = last_text[i];
+ char_event.unmodifiedText[0] = last_text[i];
+ if (render_view_->webwidget())
+ render_view_->webwidget()->handleInputEvent(char_event);
+ }
+ } else {
+ // Mimics the order of events sent by WebKit.
+ // See WebCore::Editor::setComposition() for the corresponding code.
+ focused_plugin_->HandleCompositionEnd(last_text);
+ focused_plugin_->HandleTextInput(last_text);
+ }
+ composition_text_.clear();
+}
+
+gfx::Rect PepperPluginDelegateImpl::GetCaretBounds() const {
+ if (!focused_plugin_)
+ return gfx::Rect(0, 0, 0, 0);
+ return focused_plugin_->GetCaretBounds();
+}
+
+ui::TextInputType PepperPluginDelegateImpl::GetTextInputType() const {
+ if (!focused_plugin_)
+ return ui::TEXT_INPUT_TYPE_NONE;
+ return focused_plugin_->text_input_type();
+}
+
+bool PepperPluginDelegateImpl::IsPluginAcceptingCompositionEvents() const {
+ if (!focused_plugin_)
+ return false;
+ return focused_plugin_->IsPluginAcceptingCompositionEvents();
+}
+
+bool PepperPluginDelegateImpl::CanComposeInline() const {
+ return IsPluginAcceptingCompositionEvents();
+}
+
void PepperPluginDelegateImpl::PluginCrashed(
webkit::ppapi::PluginInstance* instance) {
subscribed_to_policy_updates_.erase(instance);
@@ -1053,7 +1151,7 @@ void PepperPluginDelegateImpl::OnSetFocus(bool has_focus) {
}
bool PepperPluginDelegateImpl::IsPluginFocused() const {
- return is_pepper_plugin_focused_;
+ return focused_plugin_ != NULL;
}
void PepperPluginDelegateImpl::OnLockMouseACK(bool succeeded) {
diff --git a/content/renderer/pepper_plugin_delegate_impl.h b/content/renderer/pepper_plugin_delegate_impl.h
index af4d0de..4bcefe3 100644
--- a/content/renderer/pepper_plugin_delegate_impl.h
+++ b/content/renderer/pepper_plugin_delegate_impl.h
@@ -17,6 +17,7 @@
#include "content/common/content_export.h"
#include "ppapi/proxy/broker_dispatcher.h"
#include "ppapi/proxy/proxy_channel.h"
+#include "ui/base/ime/text_input_type.h"
#include "webkit/plugins/ppapi/plugin_delegate.h"
#include "webkit/plugins/ppapi/ppb_broker_impl.h"
#include "webkit/plugins/ppapi/ppb_flash_menu_impl.h"
@@ -46,6 +47,7 @@ class PluginModule;
namespace WebKit {
class WebFileChooserCompletion;
class WebMouseEvent;
+struct WebCompositionUnderline;
struct WebFileChooserParams;
}
@@ -166,8 +168,20 @@ class PepperPluginDelegateImpl
// notifies all of the plugins.
void OnSetFocus(bool has_focus);
- // Returns whether or not a Pepper plugin is focused.
+ // IME status.
bool IsPluginFocused() const;
+ gfx::Rect GetCaretBounds() const;
+ ui::TextInputType GetTextInputType() const;
+ bool IsPluginAcceptingCompositionEvents() const;
+ bool CanComposeInline() const;
+
+ // IME events.
+ void OnImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end);
+ void OnImeConfirmComposition(const string16& text);
// Notification that the request to lock the mouse has completed.
void OnLockMouseACK(bool succeeded);
@@ -180,7 +194,12 @@ class PepperPluginDelegateImpl
bool HandleMouseEvent(const WebKit::WebMouseEvent& event);
// PluginDelegate implementation.
- virtual void PluginFocusChanged(bool focused) OVERRIDE;
+ virtual void PluginFocusChanged(webkit::ppapi::PluginInstance* instance,
+ bool focused) OVERRIDE;
+ virtual void PluginTextInputTypeChanged(
+ webkit::ppapi::PluginInstance* instance) OVERRIDE;
+ virtual void PluginRequestedCancelComposition(
+ webkit::ppapi::PluginInstance* instance) OVERRIDE;
virtual void PluginCrashed(webkit::ppapi::PluginInstance* instance);
virtual void InstanceCreated(
webkit::ppapi::PluginInstance* instance);
@@ -359,7 +378,11 @@ class PepperPluginDelegateImpl
BrokerMap pending_connect_broker_;
// Whether or not the focus is on a PPAPI plugin
- bool is_pepper_plugin_focused_;
+ webkit::ppapi::PluginInstance* focused_plugin_;
+
+ // Current text input composition text. Empty if no composition is in
+ // progress.
+ string16 composition_text_;
// Set of instances to receive a notification when the enterprise policy has
// been updated.
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 9777dbd..fc2d729b 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -4156,6 +4156,16 @@ void RenderViewImpl::PpapiPluginFocusChanged() {
UpdateInputMethod();
}
+void RenderViewImpl::PpapiPluginTextInputTypeChanged() {
+ UpdateInputMethod();
+}
+
+void RenderViewImpl::PpapiPluginCancelComposition() {
+ Send(new ViewHostMsg_ImeCancelComposition(routing_id()));
+ ui::Range range(ui::Range::InvalidRange());
+ Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range));
+}
+
void RenderViewImpl::RequestRemoteAccessClientFirewallTraversal() {
Send(new ViewHostMsg_RequestRemoteAccessClientFirewallTraversal(routing_id_));
}
@@ -4165,13 +4175,13 @@ void RenderViewImpl::OnImeSetComposition(
const std::vector<WebKit::WebCompositionUnderline>& underlines,
int selection_start,
int selection_end) {
- // Until PPAPI has an interface for handling IME events, we skip sending
- // OnImeSetComposition. Otherwise the composition is canceled when a
- // non-editable DOM element is focused.
- //
- // TODO(kinaba) This temporal remedy can be removed after PPAPI is extended
- // with an IME handling interface.
- if (!pepper_delegate_.IsPluginFocused()) {
+ if (pepper_delegate_.IsPluginFocused()) {
+ // When a PPAPI plugin has focus, we bypass WebKit.
+ pepper_delegate_.OnImeSetComposition(text,
+ underlines,
+ selection_start,
+ selection_end);
+ } else {
#if defined(OS_WIN)
// When a plug-in has focus, we create platform-specific IME data used by
// our IME emulator and send it directly to the focused plug-in, i.e. we
@@ -4208,20 +4218,8 @@ void RenderViewImpl::OnImeSetComposition(
void RenderViewImpl::OnImeConfirmComposition(
const string16& text, const ui::Range& replacement_range) {
if (pepper_delegate_.IsPluginFocused()) {
- // TODO(kinaba) Until PPAPI has an interface for handling IME events, we
- // send character events.
- for (size_t i = 0; i < text.size(); ++i) {
- WebKit::WebKeyboardEvent char_event;
- char_event.type = WebKit::WebInputEvent::Char;
- char_event.timeStampSeconds = base::Time::Now().ToDoubleT();
- char_event.modifiers = 0;
- char_event.windowsKeyCode = text[i];
- char_event.nativeKeyCode = text[i];
- char_event.text[0] = text[i];
- char_event.unmodifiedText[0] = text[i];
- if (webwidget_)
- webwidget_->handleInputEvent(char_event);
- }
+ // When a PPAPI plugin has focus, we bypass WebKit.
+ pepper_delegate_.OnImeConfirmComposition(text);
} else {
#if defined(OS_WIN)
// Same as OnImeSetComposition(), we send the text from IMEs directly to
@@ -4242,29 +4240,18 @@ void RenderViewImpl::OnImeConfirmComposition(
}
ui::TextInputType RenderViewImpl::GetTextInputType() {
- if (pepper_delegate_.IsPluginFocused()) {
-#if !defined(TOUCH_UI)
- // TODO(kinaba) Until PPAPI has an interface for handling IME events, we
- // consider all the parts of PPAPI plugins are accepting text inputs.
- return ui::TEXT_INPUT_TYPE_TEXT;
-#else
- // Disable keyboard input in flash for touch ui until PPAPI can support IME
- // events.
- return ui::TEXT_INPUT_TYPE_NONE;
-#endif
- }
- return RenderWidget::GetTextInputType();
+ return pepper_delegate_.IsPluginFocused() ?
+ pepper_delegate_.GetTextInputType() : RenderWidget::GetTextInputType();
+}
+
+gfx::Rect RenderViewImpl::GetCaretBounds() {
+ return pepper_delegate_.IsPluginFocused() ?
+ pepper_delegate_.GetCaretBounds() : RenderWidget::GetCaretBounds();
}
bool RenderViewImpl::CanComposeInline() {
- if (pepper_delegate_.IsPluginFocused()) {
- // TODO(kinaba) Until PPAPI has an interface for handling IME events, there
- // is no way for the browser to know whether the plugin is capable of
- // drawing composition text. We assume plugins are incapable and let the
- // browser handle composition display for now.
- return false;
- }
- return true;
+ return pepper_delegate_.IsPluginFocused() ?
+ pepper_delegate_.CanComposeInline() : true;
}
#if defined(OS_WIN)
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 873c739..2869dce 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -249,6 +249,12 @@ class RenderViewImpl : public RenderWidget,
// Informs the render view that a PPAPI plugin has gained or lost focus.
void PpapiPluginFocusChanged();
+ // Informs the render view that a PPAPI plugin has changed text input status.
+ void PpapiPluginTextInputTypeChanged();
+
+ // Cancels current composition.
+ void PpapiPluginCancelComposition();
+
// Request updated policy regarding firewall NAT traversal being enabled.
void RequestRemoteAccessClientFirewallTraversal();
@@ -635,6 +641,7 @@ class RenderViewImpl : public RenderWidget,
virtual void OnImeConfirmComposition(
const string16& text, const ui::Range& replacement_range) OVERRIDE;
virtual ui::TextInputType GetTextInputType() OVERRIDE;
+ virtual gfx::Rect GetCaretBounds() OVERRIDE;
virtual bool CanComposeInline() OVERRIDE;
virtual bool WebWidgetHandlesCompositorScheduling() const OVERRIDE;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 1e608af..f228f79 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1302,10 +1302,7 @@ void RenderWidget::UpdateInputMethod() {
ui::TextInputType new_type = GetTextInputType();
bool new_can_compose_inline = CanComposeInline();
- WebRect new_caret_bounds;
-
- if (webwidget_)
- new_caret_bounds = webwidget_->caretOrSelectionBounds();
+ WebRect new_caret_bounds = GetCaretBounds();
// Only sends text input type and caret bounds to the browser process if they
// are changed.
@@ -1319,6 +1316,13 @@ void RenderWidget::UpdateInputMethod() {
}
}
+gfx::Rect RenderWidget::GetCaretBounds() {
+ if (!webwidget_)
+ return gfx::Rect();
+ return webwidget_->caretOrSelectionBounds();
+}
+
+// Check WebKit::WebTextInputType and ui::TextInputType is kept in sync.
COMPILE_ASSERT(int(WebKit::WebTextInputTypeNone) == \
int(ui::TEXT_INPUT_TYPE_NONE), mismatching_enums);
COMPILE_ASSERT(int(WebKit::WebTextInputTypeText) == \
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 397e48c..45bb14c 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -285,6 +285,7 @@ class CONTENT_EXPORT RenderWidget
// Override point to obtain that the current input method state and caret
// position.
virtual ui::TextInputType GetTextInputType();
+ virtual gfx::Rect GetCaretBounds();
// Override point to obtain that the current input method state about
// composition text.
diff --git a/ppapi/examples/ime/ime.cc b/ppapi/examples/ime/ime.cc
new file mode 100644
index 0000000..b17c9a6
--- /dev/null
+++ b/ppapi/examples/ime/ime.cc
@@ -0,0 +1,609 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "ppapi/c/dev/ppb_console_dev.h"
+#include "ppapi/c/dev/ppb_cursor_control_dev.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/dev/font_dev.h"
+#include "ppapi/cpp/dev/ime_input_event_dev.h"
+#include "ppapi/cpp/dev/text_input_dev.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/size.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+namespace {
+
+const uint32_t kTextfieldBgColor = 0xffffffff;
+const uint32_t kTextfieldTextColor = 0xff000000;
+const uint32_t kTextfieldCaretColor = 0xff000000;
+const uint32_t kTextfieldPreeditTextColor = 0xffff0000;
+const uint32_t kTextfieldUnderlineColorMain = 0xffff0000;
+const uint32_t kTextfieldUnderlineColorSub = 0xffddaaaa;
+
+void FillRect(pp::ImageData* image,
+ int left, int top, int width, int height,
+ uint32_t color) {
+ for (int y = std::max(0, top);
+ y < std::min(image->size().height() - 1, top + height);
+ ++y) {
+ for (int x = std::max(0, left);
+ x < std::min(image->size().width() - 1, left + width);
+ ++x)
+ *image->GetAddr32(pp::Point(x, y)) = color;
+ }
+}
+
+void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
+ FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
+}
+
+size_t GetPrevCharOffsetUtf8(const std::string& str, size_t current_pos) {
+ size_t i = current_pos;
+ if (i > 0) {
+ do
+ --i;
+ while (i > 0 && (str[i] & 0xc0) == 0x80);
+ }
+ return i;
+}
+
+size_t GetNextCharOffsetUtf8(const std::string& str, size_t current_pos) {
+ size_t i = current_pos;
+ if (i < str.size()) {
+ do
+ ++i;
+ while (i < str.size() && (str[i] & 0xc0) == 0x80);
+ }
+ return i;
+}
+
+size_t GetNthCharOffsetUtf8(const std::string& str, size_t n) {
+ size_t i = 0;
+ for (size_t step = 0; step < n; ++step)
+ i = GetNextCharOffsetUtf8(str, i);
+ return i;
+}
+
+} // namespace
+
+class TextFieldStatusHandler {
+ public:
+ virtual ~TextFieldStatusHandler() {}
+ virtual void FocusIn(const pp::Rect& caret, const pp::Rect& bounding_box) {}
+ virtual void FocusOut() {}
+};
+
+class TextFieldStatusNotifyingHanlder : public TextFieldStatusHandler {
+ public:
+ explicit TextFieldStatusNotifyingHanlder(pp::Instance* instance)
+ : instance_(instance),
+ textinput_control_(instance) {}
+
+ protected:
+ virtual void FocusIn(const pp::Rect& caret, const pp::Rect& bounding_box) {
+ textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
+ textinput_control_.UpdateCaretPosition(caret, bounding_box);
+ }
+ virtual void FocusOut() {
+ textinput_control_.CancelCompositionText();
+ textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
+ }
+
+ private:
+ pp::Instance* instance_;
+ pp::TextInput_Dev textinput_control_;
+};
+
+// Hand-made text field for demonstrating text input API.
+class MyTextField {
+ public:
+ MyTextField(pp::Instance* instance, TextFieldStatusHandler* handler,
+ int x, int y, int width, int height)
+ : instance_(instance),
+ status_handler_(handler),
+ area_(x, y, width, height),
+ font_size_(height - 2),
+ caret_pos_(std::string::npos) {
+ pp::FontDescription_Dev desc;
+ desc.set_family(PP_FONTFAMILY_SANSSERIF);
+ desc.set_size(font_size_);
+ font_ = pp::Font_Dev(instance_, desc);
+ }
+
+ // Paint on the specified ImageData.
+ void PaintOn(pp::ImageData* image, pp::Rect clip) {
+ clip = clip.Intersect(area_);
+ FillRect(image, clip, kTextfieldBgColor);
+
+ if (caret_pos_ != std::string::npos) {
+ int offset = area_.x();
+ // before caret
+ {
+ std::string str = utf8_text_.substr(0, caret_pos_);
+ font_.DrawTextAt(
+ image,
+ pp::TextRun_Dev(str.c_str(), false, false),
+ pp::Point(offset, area_.y() + font_size_),
+ kTextfieldTextColor,
+ clip,
+ false);
+ offset += font_.MeasureSimpleText(str);
+ }
+ // composition
+ {
+ const std::string& str = composition_;
+ font_.DrawTextAt(
+ image,
+ pp::TextRun_Dev(str.c_str(), false, false),
+ pp::Point(offset, area_.y() + font_size_),
+ kTextfieldPreeditTextColor,
+ clip,
+ false);
+ for (size_t i = 0; i < segments_.size(); ++i) {
+ size_t l = segments_[i].first;
+ size_t r = segments_[i].second;
+ if (l != r) {
+ int lx = font_.MeasureSimpleText(str.substr(0, l));
+ int rx = font_.MeasureSimpleText(str.substr(0, r));
+ FillRect(image,
+ offset + lx + 2, area_.y() + font_size_ + 1,
+ rx - lx - 4, 2,
+ i == static_cast<size_t>(target_segment_) ?
+ kTextfieldUnderlineColorMain :
+ kTextfieldUnderlineColorSub);
+ }
+ }
+ // caret
+ int caretx = font_.MeasureSimpleText(str.substr(0, selection_.first));
+ FillRect(image,
+ pp::Rect(offset + caretx, area_.y(), 2, area_.height()),
+ kTextfieldCaretColor);
+ offset += font_.MeasureSimpleText(str);
+ }
+ // after caret
+ {
+ std::string str = utf8_text_.substr(caret_pos_);
+ font_.DrawTextAt(
+ image,
+ pp::TextRun_Dev(str.c_str(), false, false),
+ pp::Point(offset, area_.y() + font_size_),
+ kTextfieldTextColor,
+ clip,
+ false);
+ }
+ } else {
+ font_.DrawTextAt(
+ image,
+ pp::TextRun_Dev(utf8_text_.c_str(), false, false),
+ pp::Point(area_.x(), area_.y() + font_size_),
+ kTextfieldTextColor,
+ clip,
+ false);
+ }
+ }
+
+ // Update current composition text.
+ void SetComposition(
+ const std::string& text,
+ const std::vector< std::pair<uint32_t, uint32_t> >& segments,
+ int32_t target_segment,
+ const std::pair<uint32_t, uint32_t>& selection) {
+ composition_ = text;
+ segments_ = segments;
+ target_segment_ = target_segment;
+ selection_ = selection;
+ CaretPosChanged();
+ }
+
+ // Is the text field focused?
+ bool Focused() const {
+ return caret_pos_ != std::string::npos;
+ }
+
+ // Does the coordinate (x,y) is contained inside the edit box?
+ bool Contains(int x, int y) const {
+ return area_.Contains(x, y);
+ }
+
+ // Resets the content text.
+ void SetText(const std::string& text) {
+ utf8_text_ = text;
+ if (Focused()) {
+ caret_pos_ = text.size();
+ CaretPosChanged();
+ }
+ }
+
+ // Inserts a text at the current caret position.
+ void InsertText(const std::string& text) {
+ if (!Focused())
+ return;
+ utf8_text_.insert(caret_pos_, text);
+ if (Focused()) {
+ caret_pos_ += text.size();
+ CaretPosChanged();
+ }
+ }
+
+ // Handles mouse click event and changes the focus state.
+ bool RefocusByMouseClick(int x, int y) {
+ if (!Contains(x, y)) {
+ // The text field is unfocused.
+ caret_pos_ = std::string::npos;
+ return false;
+ }
+
+ // The text field is focused.
+ size_t n = font_.CharacterOffsetForPixel(
+ pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
+ caret_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
+ CaretPosChanged();
+ return true;
+ }
+
+ void KeyLeft() {
+ if (!Focused())
+ return;
+ caret_pos_ = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
+ CaretPosChanged();
+ }
+
+ void KeyRight() {
+ if (!Focused())
+ return;
+ caret_pos_ = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
+ CaretPosChanged();
+ }
+
+ void KeyDelete() {
+ if (!Focused())
+ return;
+ size_t i = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
+ utf8_text_.erase(caret_pos_, i - caret_pos_);
+ CaretPosChanged();
+ }
+
+ void KeyBackspace() {
+ if (!Focused() || caret_pos_ == 0)
+ return;
+ KeyLeft();
+ KeyDelete();
+ }
+
+ private:
+ // Notify the plugin instance that the caret position has changed.
+ void CaretPosChanged() {
+ if (Focused()) {
+ std::string str = utf8_text_.substr(0, caret_pos_);
+ if (!composition_.empty())
+ str += composition_.substr(0, selection_.first);
+ int px = font_.MeasureSimpleText(str);
+ pp::Rect caret(area_.x() + px, area_.y(), 0, area_.height() + 2);
+ status_handler_->FocusIn(caret, area_);
+ }
+ }
+
+ pp::Instance* instance_;
+ TextFieldStatusHandler* status_handler_;
+
+ pp::Rect area_;
+ int font_size_;
+ pp::Font_Dev font_;
+ std::string utf8_text_;
+ size_t caret_pos_;
+ std::string composition_;
+ std::vector< std::pair<uint32_t, uint32_t> > segments_;
+ std::pair<uint32_t, uint32_t> selection_;
+ int target_segment_;
+};
+
+class MyInstance : public pp::Instance {
+ public:
+ explicit MyInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ status_handler_(new TextFieldStatusHandler) {
+ }
+
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
+ RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
+
+ for (uint32_t i = 0; i < argc; ++i) {
+ if (argn[i] == std::string("ime")) {
+ if (argv[i] == std::string("no")) {
+ // Example of NO-IME plugins (e.g., games).
+ //
+ // When a plugin never wants to accept text input, at initialization
+ // explicitly turn off the text input feature by calling:
+ pp::TextInput_Dev(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
+ } else if (argv[i] == std::string("unaware")) {
+ // Demonstrating the behavior of IME-unaware plugins.
+ // Never call any text input related APIs.
+ //
+ // In such a case, the plugin is assumed to always accept text input.
+ // For example, when the plugin is focused in touch devices a virtual
+ // keyboard may pop up, or in environment IME is used, users can type
+ // text via IME on the plugin. The characters are delivered to the
+ // plugin via PP_INPUTEVENT_TYPE_CHAR events.
+ } else if (argv[i] == std::string("caretmove")) {
+ // Demonstrating the behavior of plugins with limited IME support.
+ //
+ // It uses SetTextInputType() and UpdateCaretPosition() API to notify
+ // text input status to the browser, but unable to handle inline
+ // compositions. By using the notified information. the browser can,
+ // say, show virtual keyboards or IMEs only at appropriate timing
+ // that the plugin does need to accept text input.
+ status_handler_.reset(new TextFieldStatusNotifyingHanlder(this));
+ } else if (argv[i] == std::string("full")) {
+ // Demonstrating the behavior of plugins fully supporting IME.
+ //
+ // It notifies updates of caret positions to the browser,
+ // and handles all text input events by itself.
+ status_handler_.reset(new TextFieldStatusNotifyingHanlder(this));
+ RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
+ }
+ break;
+ }
+ }
+
+ textfield_.push_back(MyTextField(this, status_handler_.get(),
+ 10, 10, 300, 20));
+ textfield_.back().SetText("Hello");
+ textfield_.push_back(MyTextField(this, status_handler_.get(),
+ 30, 100, 300, 20));
+ textfield_.back().SetText("World");
+ return true;
+ }
+
+ protected:
+ virtual bool HandleInputEvent(const pp::InputEvent& event) {
+ bool ret = false;
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
+ const pp::MouseInputEvent mouseEvent(event);
+ ret = OnMouseDown(mouseEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
+ const pp::MouseInputEvent mouseEvent(event);
+ ret = OnMouseMove(mouseEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_KEYDOWN: {
+ Log("Keydown");
+ const pp::KeyboardInputEvent keyEvent(event);
+ ret = OnKeyDown(keyEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_CHAR: {
+ const pp::KeyboardInputEvent keyEvent(event);
+ Log("Char [" + keyEvent.GetCharacterText().AsString() + "]");
+ ret = OnChar(keyEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: {
+ const pp::IMEInputEvent_Dev imeEvent(event);
+ Log("CompositionStart [" + imeEvent.GetText().AsString() + "]");
+ ret = true;
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: {
+ const pp::IMEInputEvent_Dev imeEvent(event);
+ Log("CompositionUpdate [" + imeEvent.GetText().AsString() + "]");
+ ret = OnCompositionUpdate(imeEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: {
+ const pp::IMEInputEvent_Dev imeEvent(event);
+ Log("CompositionEnd [" + imeEvent.GetText().AsString() + "]");
+ ret = OnCompositionEnd(imeEvent);
+ break;
+ }
+ case PP_INPUTEVENT_TYPE_IME_TEXT: {
+ const pp::IMEInputEvent_Dev imeEvent(event);
+ Log("ImeText [" + imeEvent.GetText().AsString() + "]");
+ ret = OnImeText(imeEvent);
+ break;
+ }
+ default:
+ break;
+ }
+ if (ret && event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE)
+ Paint();
+ return ret;
+ }
+
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
+ if (position.size() == last_size_)
+ return;
+ last_size_ = position.size();
+ Paint();
+ }
+
+ private:
+ bool OnCompositionUpdate(const pp::IMEInputEvent_Dev& ev) {
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->Focused()) {
+ std::vector< std::pair<uint32_t, uint32_t> > segs;
+ for (uint32_t i = 0; i < ev.GetSegmentNumber(); ++i)
+ segs.push_back(std::make_pair(ev.GetSegmentOffset(i),
+ ev.GetSegmentOffset(i + 1)));
+ it->SetComposition(ev.GetText().AsString(),
+ segs,
+ ev.GetTargetSegment(),
+ ev.GetSelection());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool OnCompositionEnd(const pp::IMEInputEvent_Dev& ev) {
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->Focused()) {
+ it->SetComposition("", std::vector< std::pair<uint32_t, uint32_t> >(),
+ 0, std::make_pair(0, 0));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool OnMouseDown(const pp::MouseInputEvent& ev) {
+ bool anyone_focused = false;
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->RefocusByMouseClick(ev.GetPosition().x(),
+ ev.GetPosition().y())) {
+ anyone_focused = true;
+ }
+ }
+ if (!anyone_focused)
+ status_handler_->FocusOut();
+ return true;
+ }
+
+ bool OnMouseMove(const pp::MouseInputEvent& ev) {
+ const PPB_CursorControl_Dev* cursor_control =
+ reinterpret_cast<const PPB_CursorControl_Dev*>(
+ pp::Module::Get()->GetBrowserInterface(
+ PPB_CURSOR_CONTROL_DEV_INTERFACE));
+ if (!cursor_control)
+ return false;
+
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->Contains(ev.GetPosition().x(),
+ ev.GetPosition().y())) {
+ cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
+ 0, NULL);
+ return true;
+ }
+ }
+ cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
+ 0, NULL);
+ return true;
+ }
+
+ bool OnKeyDown(const pp::KeyboardInputEvent& ev) {
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->Focused()) {
+ switch (ev.GetKeyCode()) {
+ case ui::VKEY_LEFT:
+ it->KeyLeft();
+ break;
+ case ui::VKEY_RIGHT:
+ it->KeyRight();
+ break;
+ case ui::VKEY_DELETE:
+ it->KeyDelete();
+ break;
+ case ui::VKEY_BACK:
+ it->KeyBackspace();
+ break;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool OnChar(const pp::KeyboardInputEvent& ev) {
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->Focused()) {
+ std::string str = ev.GetCharacterText().AsString();
+ if (str != "\r" && str != "\n")
+ it->InsertText(str);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool OnImeText(const pp::IMEInputEvent_Dev ev) {
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ if (it->Focused()) {
+ it->InsertText(ev.GetText().AsString());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void Paint() {
+ pp::Rect clip(0, 0, last_size_.width(), last_size_.height());
+ PaintClip(clip);
+ }
+
+ void PaintClip(const pp::Rect& clip) {
+ pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_, true);
+ pp::Graphics2D device(this, last_size_, false);
+ BindGraphics(device);
+
+ for (std::vector<MyTextField>::iterator it = textfield_.begin();
+ it != textfield_.end();
+ ++it) {
+ it->PaintOn(&image, clip);
+ }
+
+ device.PaintImageData(image, pp::Point(0, 0));
+ device.Flush(pp::CompletionCallback(&OnFlush, this));
+ }
+
+ static void OnFlush(void* user_data, int32_t result) {}
+
+ // Prints a debug message.
+ void Log(const pp::Var& value) {
+ const PPB_Console_Dev* console = reinterpret_cast<const PPB_Console_Dev*>(
+ pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE));
+ if (!console)
+ return;
+ console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
+ }
+
+ // IME Control interface.
+ scoped_ptr<TextFieldStatusHandler> status_handler_;
+
+ // Remembers the size of this instance.
+ pp::Size last_size_;
+
+ // Holds instances of text fields.
+ std::vector<MyTextField> textfield_;
+};
+
+class MyModule : public pp::Module {
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new MyInstance(instance);
+ }
+};
+
+namespace pp {
+
+Module* CreateModule() {
+ return new MyModule();
+}
+
+} // namespace pp
diff --git a/ppapi/examples/ime/ime.html b/ppapi/examples/ime/ime.html
new file mode 100644
index 0000000..a9f056c
--- /dev/null
+++ b/ppapi/examples/ime/ime.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+<title>IME Example</title>
+</head>
+<body style="background-color:#eef">
+ <p>Full IME Support</p>
+ <object id="plugin1" type="application/x-ppapi-example" width="400" height="150"
+ border="2px"><param name="ime" value="full" /></object>
+
+ <p>CaretMoveOnly</p>
+ <object id="plugin2" type="application/x-ppapi-example" width="400" height="150"
+ border="2px"><param name="ime" value="caretmove" /></object>
+
+ <p>IME-Unaware</p>
+ <object id="plugin3" type="application/x-ppapi-example" width="400" height="150"
+ border="2px"><param name="ime" value="unaware" /></object>
+
+ <p>No IME (explicitly turn IME off)</p>
+ <object id="plugin4" type="application/x-ppapi-example" width="400" height="150"
+ border="2px"><param name="ime" value="no" /></object>
+
+ <p>HTML Textarea</p>
+ <textarea width="200" height="3"></textarea>
+</body>
+</html>
diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi
index c5f2aa8..08e661e 100644
--- a/ppapi/ppapi_tests.gypi
+++ b/ppapi/ppapi_tests.gypi
@@ -313,6 +313,16 @@
],
},
{
+ 'target_name': 'ppapi_example_ime',
+ 'dependencies': [
+ 'ppapi_example_skeleton',
+ 'ppapi.gyp:ppapi_cpp',
+ ],
+ 'sources': [
+ 'examples/ime/ime.cc',
+ ],
+ },
+ {
'target_name': 'ppapi_example_paint_manager',
'dependencies': [
'ppapi_example_skeleton',
diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi
index 83b09fd..89c42c2 100644
--- a/webkit/glue/webkit_glue.gypi
+++ b/webkit/glue/webkit_glue.gypi
@@ -116,6 +116,7 @@
'<(DEPTH)/third_party/icu/icu.gyp:icui18n',
'<(DEPTH)/third_party/icu/icu.gyp:icuuc',
'<(DEPTH)/third_party/npapi/npapi.gyp:npapi',
+ '<(DEPTH)/ui/ui.gyp:ui',
'<(DEPTH)/v8/tools/gyp/v8.gyp:v8',
'webkit_resources',
'webkit_strings',
diff --git a/webkit/plugins/ppapi/DEPS b/webkit/plugins/ppapi/DEPS
index ffafa6f..a2b6af6 100644
--- a/webkit/plugins/ppapi/DEPS
+++ b/webkit/plugins/ppapi/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+ppapi/thunk",
"+printing",
"+media/video",
+ "+ui/base/ime",
# This should technically not be allowed. Brett is refactoring this and will
# move this file to a more proper shared location in a future iteration.
diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.cc b/webkit/plugins/ppapi/mock_plugin_delegate.cc
index 09cc038..8aeacdd 100644
--- a/webkit/plugins/ppapi/mock_plugin_delegate.cc
+++ b/webkit/plugins/ppapi/mock_plugin_delegate.cc
@@ -18,7 +18,15 @@ MockPluginDelegate::MockPluginDelegate() {
MockPluginDelegate::~MockPluginDelegate() {
}
-void MockPluginDelegate::PluginFocusChanged(bool focused) {
+void MockPluginDelegate::PluginFocusChanged(PluginInstance* instance,
+ bool focused) {
+}
+
+void MockPluginDelegate::PluginTextInputTypeChanged(PluginInstance* instance) {
+}
+
+void MockPluginDelegate::PluginRequestedCancelComposition(
+ PluginInstance* instance) {
}
void MockPluginDelegate::PluginCrashed(PluginInstance* instance) {
diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.h b/webkit/plugins/ppapi/mock_plugin_delegate.h
index 29c989b..bef5340 100644
--- a/webkit/plugins/ppapi/mock_plugin_delegate.h
+++ b/webkit/plugins/ppapi/mock_plugin_delegate.h
@@ -15,7 +15,9 @@ class MockPluginDelegate : public PluginDelegate {
MockPluginDelegate();
virtual ~MockPluginDelegate();
- virtual void PluginFocusChanged(bool focused);
+ virtual void PluginFocusChanged(PluginInstance* instance, bool focused);
+ virtual void PluginTextInputTypeChanged(PluginInstance* instance);
+ virtual void PluginRequestedCancelComposition(PluginInstance* instance);
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 0cbeabf..3200431 100644
--- a/webkit/plugins/ppapi/plugin_delegate.h
+++ b/webkit/plugins/ppapi/plugin_delegate.h
@@ -246,7 +246,14 @@ class PluginDelegate {
};
// Notification that the given plugin is focused or unfocused.
- virtual void PluginFocusChanged(bool focused) = 0;
+ virtual void PluginFocusChanged(webkit::ppapi::PluginInstance* instance,
+ bool focused) = 0;
+ // Notification that the text input status of the given plugin is changed.
+ virtual void PluginTextInputTypeChanged(
+ webkit::ppapi::PluginInstance* instance) = 0;
+ // Notification that the plugin requested to cancel the current composition.
+ virtual void PluginRequestedCancelComposition(
+ webkit::ppapi::PluginInstance* instance) = 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 9824c67..34717e1 100644
--- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc
+++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc
@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
+#include "base/utf_offset_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "ppapi/c/dev/ppb_console_dev.h"
#include "ppapi/c/dev/ppb_find_dev.h"
@@ -34,6 +35,7 @@
#include "ppapi/c/private/ppp_instance_private.h"
#include "ppapi/shared_impl/input_event_impl.h"
#include "ppapi/shared_impl/resource.h"
+#include "ppapi/shared_impl/time_conversion.h"
#include "ppapi/shared_impl/url_util_impl.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/thunk/enter.h"
@@ -134,6 +136,20 @@ typedef bool (*RenderPDFPageToDCProc)(
namespace {
+#if !defined(TOUCH_UI)
+// The default text input type is to regard the plugin always accept text input.
+// This is for allowing users to use input methods even on completely-IME-
+// unaware plugins (e.g., PPAPI Flash or PDF plugin for M16).
+// Plugins need to explicitly opt out the text input mode if they know
+// that they don't accept texts.
+const ui::TextInputType kPluginDefaultTextInputType = ui::TEXT_INPUT_TYPE_TEXT;
+#else
+// On the other hand, for touch ui, accepting text input implies to pop up
+// virtual keyboard always. It makes IME-unaware plugins almost unusable,
+// and hence is disabled by default (codereview.chromium.org/7800044).
+const ui::TextInputType kPluginDefaultTextInputType = ui::TEXT_INPUT_TYPE_NONE;
+#endif
+
#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \
COMPILE_ASSERT(static_cast<int>(WebCursorInfo::webkit_name) \
== static_cast<int>(np_name), \
@@ -266,6 +282,10 @@ PluginInstance::PluginInstance(
sad_plugin_(NULL),
input_event_mask_(0),
filtered_input_event_mask_(0),
+ text_input_type_(kPluginDefaultTextInputType),
+ text_input_caret_(0, 0, 0, 0),
+ text_input_caret_bounds_(0, 0, 0, 0),
+ text_input_caret_set_(false),
lock_mouse_callback_(PP_BlockUntilComplete()) {
pp_instance_ = ResourceTracker::Get()->AddInstance(this);
@@ -475,6 +495,148 @@ bool PluginInstance::HandleDocumentLoad(PPB_URLLoader_Impl* loader) {
pp_instance(), loader->pp_resource()));
}
+bool PluginInstance::SendCompositionEventToPlugin(PP_InputEvent_Type type,
+ const string16& text) {
+ std::vector<WebKit::WebCompositionUnderline> empty;
+ return SendCompositionEventWithUnderlineInformationToPlugin(
+ type, text, empty, static_cast<int>(text.size()),
+ static_cast<int>(text.size()));
+}
+
+bool PluginInstance::SendCompositionEventWithUnderlineInformationToPlugin(
+ PP_InputEvent_Type type,
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end) {
+ // Keep a reference on the stack. See NOTE above.
+ scoped_refptr<PluginInstance> ref(this);
+
+ if (!LoadInputEventInterface())
+ return false;
+
+ PP_InputEvent_Class event_class = PP_INPUTEVENT_CLASS_IME;
+ if (!(filtered_input_event_mask_ & event_class) &&
+ !(input_event_mask_ & event_class))
+ return false;
+
+ ::ppapi::InputEventData event;
+ event.event_type = type;
+ event.event_time_stamp = ::ppapi::TimeTicksToPPTimeTicks(
+ base::TimeTicks::Now());
+
+ // Convert UTF16 text to UTF8 with offset conversion.
+ std::vector<size_t> utf16_offsets;
+ utf16_offsets.push_back(selection_start);
+ utf16_offsets.push_back(selection_end);
+ for (size_t i = 0; i < underlines.size(); ++i) {
+ utf16_offsets.push_back(underlines[i].startOffset);
+ utf16_offsets.push_back(underlines[i].endOffset);
+ }
+ std::vector<size_t> utf8_offsets(utf16_offsets);
+ event.character_text = UTF16ToUTF8AndAdjustOffsets(text, &utf8_offsets);
+
+ // Set the converted selection range.
+ event.composition_selection_start = (utf8_offsets[0] == std::string::npos ?
+ event.character_text.size() : utf8_offsets[0]);
+ event.composition_selection_end = (utf8_offsets[1] == std::string::npos ?
+ event.character_text.size() : utf8_offsets[1]);
+
+ // Set the converted segmentation points.
+ // Be sure to add 0 and size(), and remove duplication or errors.
+ std::set<size_t> offset_set(utf8_offsets.begin()+2, utf8_offsets.end());
+ offset_set.insert(0);
+ offset_set.insert(event.character_text.size());
+ offset_set.erase(std::string::npos);
+ event.composition_segment_offsets.assign(offset_set.begin(),
+ offset_set.end());
+
+ // Set the composition target.
+ for (size_t i = 0; i < underlines.size(); ++i) {
+ if (underlines[i].thick) {
+ std::vector<uint32_t>::iterator it =
+ std::find(event.composition_segment_offsets.begin(),
+ event.composition_segment_offsets.end(),
+ utf8_offsets[2*i+2]);
+ if (it != event.composition_segment_offsets.end()) {
+ event.composition_target_segment =
+ it - event.composition_segment_offsets.begin();
+ break;
+ }
+ }
+ }
+
+ // Send the event.
+ bool handled = false;
+ if (filtered_input_event_mask_ & event_class)
+ event.is_filtered = true;
+ else
+ handled = true; // Unfiltered events are assumed to be handled.
+ scoped_refptr<InputEventImpl> event_resource(
+ new InputEventImpl(InputEventImpl::InitAsImpl(),
+ pp_instance(), event));
+ handled |= PP_ToBool(plugin_input_event_interface_->HandleInputEvent(
+ pp_instance(), event_resource->pp_resource()));
+ return handled;
+}
+
+bool PluginInstance::HandleCompositionStart(const string16& text) {
+ return SendCompositionEventToPlugin(PP_INPUTEVENT_TYPE_IME_COMPOSITION_START,
+ text);
+}
+
+bool PluginInstance::HandleCompositionUpdate(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end) {
+ return SendCompositionEventWithUnderlineInformationToPlugin(
+ PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE,
+ text, underlines, selection_start, selection_end);
+}
+
+bool PluginInstance::HandleCompositionEnd(const string16& text) {
+ return SendCompositionEventToPlugin(PP_INPUTEVENT_TYPE_IME_COMPOSITION_END,
+ text);
+}
+
+bool PluginInstance::HandleTextInput(const string16& text) {
+ return SendCompositionEventToPlugin(PP_INPUTEVENT_TYPE_IME_TEXT,
+ text);
+}
+
+void PluginInstance::UpdateCaretPosition(const gfx::Rect& caret,
+ const gfx::Rect& bounding_box) {
+ text_input_caret_ = caret;
+ text_input_caret_bounds_ = bounding_box;
+ text_input_caret_set_ = true;
+}
+
+void PluginInstance::SetTextInputType(ui::TextInputType type) {
+ text_input_type_ = type;
+}
+
+bool PluginInstance::IsPluginAcceptingCompositionEvents() const {
+ return (filtered_input_event_mask_ & PP_INPUTEVENT_CLASS_IME) ||
+ (input_event_mask_ & PP_INPUTEVENT_CLASS_IME);
+}
+
+gfx::Rect PluginInstance::GetCaretBounds() const {
+ if (!text_input_caret_set_) {
+ // If it is never set by the plugin, use the bottom left corner.
+ return gfx::Rect(position().x(), position().y()+position().height(), 0, 0);
+ }
+
+ // TODO(kinaba) Take CSS transformation into accont.
+ // TODO(kinaba) Take bounding_box into account. On some platforms, an
+ // "exclude rectangle" where candidate window must avoid the region can be
+ // passed to IME. Currently, we pass only the caret rectangle because
+ // it is the only information supported uniformly in Chromium.
+ gfx::Rect caret(text_input_caret_);
+ caret.Offset(position().origin());
+ return caret;
+}
+
bool PluginInstance::HandleInputEvent(const WebKit::WebInputEvent& event,
WebCursorInfo* cursor_info) {
TRACE_EVENT0("ppapi", "PluginInstance::HandleInputEvent");
@@ -583,7 +745,7 @@ void PluginInstance::SetWebKitFocus(bool has_focus) {
bool old_plugin_focus = PluginHasFocus();
has_webkit_focus_ = has_focus;
if (PluginHasFocus() != old_plugin_focus) {
- delegate()->PluginFocusChanged(PluginHasFocus());
+ delegate()->PluginFocusChanged(this, PluginHasFocus());
instance_interface_->DidChangeFocus(pp_instance(),
PP_FromBool(PluginHasFocus()));
}
diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.h b/webkit/plugins/ppapi/ppapi_plugin_instance.h
index a7356b6..00fbfab 100644
--- a/webkit/plugins/ppapi/ppapi_plugin_instance.h
+++ b/webkit/plugins/ppapi/ppapi_plugin_instance.h
@@ -22,6 +22,7 @@
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppb_input_event.h"
#include "ppapi/c/ppp_graphics_3d.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/shared_impl/function_group_base.h"
@@ -31,6 +32,7 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCanvas.h"
+#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/rect.h"
#include "webkit/plugins/ppapi/plugin_delegate.h"
@@ -48,14 +50,11 @@ struct PPP_Zoom_Dev;
class SkBitmap;
class TransportDIB;
-namespace gfx {
-class Rect;
-}
-
namespace WebKit {
-struct WebCursorInfo;
class WebInputEvent;
class WebPluginContainer;
+struct WebCompositionUnderline;
+struct WebCursorInfo;
}
namespace ppapi {
@@ -167,6 +166,26 @@ class PluginInstance : public base::RefCounted<PluginInstance>,
PP_Var GetInstanceObject();
void ViewChanged(const gfx::Rect& position, const gfx::Rect& clip);
+ // Handlers for composition events.
+ bool HandleCompositionStart(const string16& text);
+ bool HandleCompositionUpdate(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end);
+ bool HandleCompositionEnd(const string16& text);
+ bool HandleTextInput(const string16& text);
+
+ // Implementation of composition API.
+ void UpdateCaretPosition(const gfx::Rect& caret,
+ const gfx::Rect& bounding_box);
+ void SetTextInputType(ui::TextInputType type);
+
+ // Gets the current text input status.
+ ui::TextInputType text_input_type() const { return text_input_type_; }
+ gfx::Rect GetCaretBounds() const;
+ bool IsPluginAcceptingCompositionEvents() const;
+
// Notifications about focus changes, see has_webkit_focus_ below.
void SetWebKitFocus(bool has_focus);
void SetContentAreaFocus(bool has_focus);
@@ -412,6 +431,17 @@ class PluginInstance : public base::RefCounted<PluginInstance>,
void DoSetCursor(WebKit::WebCursorInfo* cursor);
+ // Internal helper functions for HandleCompositionXXX().
+ bool SendCompositionEventToPlugin(
+ PP_InputEvent_Type type,
+ const string16& text);
+ bool SendCompositionEventWithUnderlineInformationToPlugin(
+ PP_InputEvent_Type type,
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end);
+
// Checks if the security origin of the document containing this instance can
// assess the security origin of the main frame document.
bool CanAccessMainFrame() const;
@@ -555,6 +585,12 @@ class PluginInstance : public base::RefCounted<PluginInstance>,
uint32_t input_event_mask_;
uint32_t filtered_input_event_mask_;
+ // Text composition status.
+ ui::TextInputType text_input_type_;
+ gfx::Rect text_input_caret_;
+ gfx::Rect text_input_caret_bounds_;
+ bool text_input_caret_set_;
+
PP_CompletionCallback lock_mouse_callback_;
DISALLOW_COPY_AND_ASSIGN(PluginInstance);
diff --git a/webkit/plugins/ppapi/ppb_text_input_impl.cc b/webkit/plugins/ppapi/ppb_text_input_impl.cc
index fc02925..9a1cc92 100644
--- a/webkit/plugins/ppapi/ppb_text_input_impl.cc
+++ b/webkit/plugins/ppapi/ppb_text_input_impl.cc
@@ -5,7 +5,8 @@
#include "webkit/plugins/ppapi/ppb_text_input_impl.h"
#include "base/logging.h"
-#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextInputType.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/gfx/rect.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
namespace webkit {
@@ -20,39 +21,44 @@ PPB_TextInput_Impl::AsPPB_TextInput_FunctionAPI() {
return this;
}
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeNone) == \
+// Check PP_TextInput_Type and ui::TextInputType are kept in sync.
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_NONE) == \
int(PP_TEXTINPUT_TYPE_NONE), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeText) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_TEXT) == \
int(PP_TEXTINPUT_TYPE_TEXT), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypePassword) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_PASSWORD) == \
int(PP_TEXTINPUT_TYPE_PASSWORD), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeSearch) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_SEARCH) == \
int(PP_TEXTINPUT_TYPE_SEARCH), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeEmail) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_EMAIL) == \
int(PP_TEXTINPUT_TYPE_EMAIL), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeNumber) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_NUMBER) == \
int(PP_TEXTINPUT_TYPE_NUMBER), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeTelephone) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_TELEPHONE) == \
int(PP_TEXTINPUT_TYPE_TELEPHONE), mismatching_enums);
-COMPILE_ASSERT(int(WebKit::WebTextInputTypeURL) == \
+COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_URL) == \
int(PP_TEXTINPUT_TYPE_URL), mismatching_enums);
void PPB_TextInput_Impl::SetTextInputType(PP_Instance instance,
PP_TextInput_Type type) {
- // TODO(kinaba) the implementation is split to another CL for reviewing.
- NOTIMPLEMENTED();
+ int itype = type;
+ if (itype < 0 || itype > ui::TEXT_INPUT_TYPE_URL)
+ itype = ui::TEXT_INPUT_TYPE_NONE;
+ instance_->SetTextInputType(static_cast<ui::TextInputType>(itype));
}
void PPB_TextInput_Impl::UpdateCaretPosition(PP_Instance instance,
const PP_Rect& caret,
const PP_Rect& bounding_box) {
- // TODO(kinaba) the implementation is split to another CL for reviewing.
- NOTIMPLEMENTED();
+ instance_->UpdateCaretPosition(
+ gfx::Rect(caret.point.x, caret.point.y,
+ caret.size.width, caret.size.height),
+ gfx::Rect(bounding_box.point.x, bounding_box.point.y,
+ bounding_box.size.width, bounding_box.size.height));
}
void PPB_TextInput_Impl::CancelCompositionText(PP_Instance instance) {
- // TODO(kinaba) the implementation is split to another CL for reviewing.
- NOTIMPLEMENTED();
+ instance_->delegate()->PluginRequestedCancelComposition(instance_);
}
} // namespace ppapi