1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <string>
7#include <utility>
8#include <vector>
9
10#include "ppapi/c/dev/ppb_cursor_control_dev.h"
11#include "ppapi/c/ppb_console.h"
12#include "ppapi/cpp/completion_callback.h"
13#include "ppapi/cpp/dev/font_dev.h"
14#include "ppapi/cpp/graphics_2d.h"
15#include "ppapi/cpp/image_data.h"
16#include "ppapi/cpp/input_event.h"
17#include "ppapi/cpp/instance.h"
18#include "ppapi/cpp/module.h"
19#include "ppapi/cpp/rect.h"
20#include "ppapi/cpp/size.h"
21#include "ppapi/cpp/text_input_controller.h"
22
23namespace {
24
25// Extracted from: ui/events/keycodes/keyboard_codes.h
26enum {
27  VKEY_BACK = 0x08,
28  VKEY_SHIFT = 0x10,
29  VKEY_DELETE = 0x2E,
30  VKEY_LEFT = 0x25,
31  VKEY_UP = 0x26,
32  VKEY_RIGHT = 0x27,
33  VKEY_DOWN = 0x28,
34};
35
36const uint32_t kTextfieldBgColor = 0xffffffff;
37const uint32_t kTextfieldTextColor = 0xff000000;
38const uint32_t kTextfieldCaretColor = 0xff000000;
39const uint32_t kTextfieldPreeditTextColor = 0xffff0000;
40const uint32_t kTextfieldSelectionBackgroundColor = 0xffeecccc;
41const uint32_t kTextfieldUnderlineColorMain = 0xffff0000;
42const uint32_t kTextfieldUnderlineColorSub = 0xffddaaaa;
43
44void FillRect(pp::ImageData* image,
45              int left, int top, int width, int height,
46              uint32_t color) {
47  for (int y = std::max(0, top);
48       y < std::min(image->size().height() - 1, top + height);
49       ++y) {
50    for (int x = std::max(0, left);
51         x < std::min(image->size().width() - 1, left + width);
52         ++x)
53      *image->GetAddr32(pp::Point(x, y)) = color;
54  }
55}
56
57void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
58  FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
59}
60
61size_t GetPrevCharOffsetUtf8(const std::string& str, size_t current_pos) {
62  size_t i = current_pos;
63  if (i > 0) {
64    do
65      --i;
66    while (i > 0 && (str[i] & 0xc0) == 0x80);
67  }
68  return i;
69}
70
71size_t GetNextCharOffsetUtf8(const std::string& str, size_t current_pos) {
72  size_t i = current_pos;
73  if (i < str.size()) {
74    do
75      ++i;
76    while (i < str.size() && (str[i] & 0xc0) == 0x80);
77  }
78  return i;
79}
80
81size_t GetNthCharOffsetUtf8(const std::string& str, size_t n) {
82  size_t i = 0;
83  for (size_t step = 0; step < n; ++step)
84    i = GetNextCharOffsetUtf8(str, i);
85  return i;
86}
87
88}  // namespace
89
90class TextFieldStatusHandler {
91 public:
92  virtual ~TextFieldStatusHandler() {}
93  virtual void FocusIn(const pp::Rect& caret) {}
94  virtual void FocusOut() {}
95  virtual void UpdateSelection(const std::string& text) {}
96};
97
98class TextFieldStatusNotifyingHandler : public TextFieldStatusHandler {
99 public:
100  explicit TextFieldStatusNotifyingHandler(pp::Instance* instance)
101      : textinput_control_(instance) {
102  }
103
104 protected:
105  // Implement TextFieldStatusHandler.
106  virtual void FocusIn(const pp::Rect& caret) {
107    textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
108    textinput_control_.UpdateCaretPosition(caret);
109  }
110  virtual void FocusOut() {
111    textinput_control_.CancelCompositionText();
112    textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
113  }
114  virtual void UpdateSelection(const std::string& text) {
115    textinput_control_.UpdateSurroundingText(text, 0, text.size());
116  }
117
118 private:
119  pp::TextInputController textinput_control_;
120};
121
122// Hand-made text field for demonstrating text input API.
123class MyTextField {
124 public:
125  MyTextField(pp::Instance* instance, TextFieldStatusHandler* handler,
126              int x, int y, int width, int height)
127      : instance_(instance),
128        status_handler_(handler),
129        area_(x, y, width, height),
130        font_size_(height - 2),
131        caret_pos_(std::string::npos),
132        anchor_pos_(std::string::npos),
133        target_segment_(0) {
134    pp::FontDescription_Dev desc;
135    desc.set_family(PP_FONTFAMILY_SANSSERIF);
136    desc.set_size(font_size_);
137    font_ = pp::Font_Dev(instance_, desc);
138  }
139
140  // Paint on the specified ImageData.
141  void PaintOn(pp::ImageData* image, pp::Rect clip) {
142    clip = clip.Intersect(area_);
143    FillRect(image, clip, kTextfieldBgColor);
144
145    if (caret_pos_ != std::string::npos) {
146      int offset = area_.x();
147      // selection (for the case without composition text)
148      if (composition_.empty() && HasSelection()) {
149        int left_x = font_.MeasureSimpleText(
150            utf8_text_.substr(0, SelectionLeft()));
151        int right_x = font_.MeasureSimpleText(
152            utf8_text_.substr(0, SelectionRight()));
153        FillRect(image, offset + left_x, area_.y(), right_x - left_x,
154                 area_.height(), kTextfieldSelectionBackgroundColor);
155      }
156      // before caret
157      {
158        std::string str = utf8_text_.substr(0, caret_pos_);
159        font_.DrawTextAt(
160            image,
161            pp::TextRun_Dev(str.c_str(), false, false),
162            pp::Point(offset, area_.y() + font_size_),
163            kTextfieldTextColor,
164            clip,
165            false);
166        offset += font_.MeasureSimpleText(str);
167      }
168      // composition
169      {
170        const std::string& str = composition_;
171        // selection
172        if (composition_selection_.first != composition_selection_.second) {
173          int left_x = font_.MeasureSimpleText(
174              str.substr(0, composition_selection_.first));
175          int right_x = font_.MeasureSimpleText(
176              str.substr(0, composition_selection_.second));
177          FillRect(image, offset + left_x, area_.y(), right_x - left_x,
178                   area_.height(), kTextfieldSelectionBackgroundColor);
179        }
180        // composition text
181        font_.DrawTextAt(
182            image,
183            pp::TextRun_Dev(str.c_str(), false, false),
184            pp::Point(offset, area_.y() + font_size_),
185            kTextfieldPreeditTextColor,
186            clip,
187            false);
188        for (size_t i = 0; i < segments_.size(); ++i) {
189          size_t l = segments_[i].first;
190          size_t r = segments_[i].second;
191          if (l != r) {
192            int lx = font_.MeasureSimpleText(str.substr(0, l));
193            int rx = font_.MeasureSimpleText(str.substr(0, r));
194            FillRect(image,
195                     offset + lx + 2, area_.y() + font_size_ + 1,
196                     rx - lx - 4, 2,
197                     i == static_cast<size_t>(target_segment_) ?
198                         kTextfieldUnderlineColorMain :
199                         kTextfieldUnderlineColorSub);
200          }
201        }
202        // caret
203        int caretx = font_.MeasureSimpleText(
204            str.substr(0, composition_selection_.first));
205        FillRect(image,
206                 pp::Rect(offset + caretx, area_.y(), 2, area_.height()),
207                 kTextfieldCaretColor);
208        offset += font_.MeasureSimpleText(str);
209      }
210      // after caret
211      {
212        std::string str = utf8_text_.substr(caret_pos_);
213        font_.DrawTextAt(
214            image,
215            pp::TextRun_Dev(str.c_str(), false, false),
216            pp::Point(offset, area_.y() + font_size_),
217            kTextfieldTextColor,
218            clip,
219            false);
220      }
221    } else {
222      font_.DrawTextAt(
223          image,
224          pp::TextRun_Dev(utf8_text_.c_str(), false, false),
225          pp::Point(area_.x(), area_.y() + font_size_),
226          kTextfieldTextColor,
227          clip,
228          false);
229    }
230  }
231
232  // Update current composition text.
233  void SetComposition(
234      const std::string& text,
235      const std::vector< std::pair<uint32_t, uint32_t> >& segments,
236      int32_t target_segment,
237      const std::pair<uint32_t, uint32_t>& selection) {
238    if (HasSelection() && !text.empty())
239      InsertText(std::string());
240    composition_ = text;
241    segments_ = segments;
242    target_segment_ = target_segment;
243    composition_selection_ = selection;
244    CaretPosChanged();
245  }
246
247  // Is the text field focused?
248  bool Focused() const {
249    return caret_pos_ != std::string::npos;
250  }
251
252  // Does the coordinate (x,y) is contained inside the edit box?
253  bool Contains(int x, int y) const {
254    return area_.Contains(x, y);
255  }
256
257  // Resets the content text.
258  void SetText(const std::string& text) {
259    utf8_text_ = text;
260    if (Focused()) {
261      caret_pos_ = anchor_pos_ = text.size();
262      CaretPosChanged();
263    }
264  }
265
266  // Inserts a text at the current caret position.
267  void InsertText(const std::string& text) {
268    if (!Focused())
269      return;
270    utf8_text_.replace(SelectionLeft(), SelectionRight() - SelectionLeft(),
271                       text);
272    caret_pos_ = anchor_pos_ = SelectionLeft() + text.size();
273    CaretPosChanged();
274  }
275
276  // Handles mouse click event and changes the focus state.
277  bool RefocusByMouseClick(int x, int y) {
278    if (!Contains(x, y)) {
279      // The text field is unfocused.
280      caret_pos_ = anchor_pos_ = std::string::npos;
281      return false;
282    }
283
284    // The text field is focused.
285    size_t n = font_.CharacterOffsetForPixel(
286        pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
287    caret_pos_ = anchor_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
288    CaretPosChanged();
289    return true;
290  }
291
292  void MouseDrag(int x, int y) {
293    if (!Focused())
294      return;
295    size_t n = font_.CharacterOffsetForPixel(
296        pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
297    caret_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
298  }
299
300  void MouseUp(int x, int y) {
301    if (!Focused())
302      return;
303    CaretPosChanged();
304  }
305
306  void KeyLeft(bool shift) {
307    if (!Focused())
308      return;
309    // Move caret to the head of the selection or to the previous character.
310    if (!shift && HasSelection())
311      caret_pos_ = SelectionLeft();
312    else
313      caret_pos_ = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
314    // Move the anchor if the shift key is not pressed.
315    if (!shift)
316      anchor_pos_ = caret_pos_;
317    CaretPosChanged();
318  }
319
320  void KeyRight(bool shift) {
321    if (!Focused())
322      return;
323    // Move caret to the end of the selection or to the next character.
324    if (!shift && HasSelection())
325      caret_pos_ = SelectionRight();
326    else
327      caret_pos_ = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
328    // Move the anchor if the shift key is not pressed.
329    if (!shift)
330      anchor_pos_ = caret_pos_;
331    CaretPosChanged();
332  }
333
334  void KeyDelete() {
335    if (!Focused())
336      return;
337    if (HasSelection()) {
338      InsertText(std::string());
339    } else {
340      size_t i = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
341      utf8_text_.erase(caret_pos_, i - caret_pos_);
342      CaretPosChanged();
343    }
344  }
345
346  void KeyBackspace() {
347    if (!Focused())
348      return;
349    if (HasSelection()) {
350      InsertText(std::string());
351    } else if (caret_pos_ != 0) {
352      size_t i = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
353      utf8_text_.erase(i, caret_pos_ - i);
354      caret_pos_ = anchor_pos_ = i;
355      CaretPosChanged();
356    }
357  }
358
359 private:
360  // Notify the plugin instance that the caret position has changed.
361  void CaretPosChanged() {
362    if (Focused()) {
363      std::string str = utf8_text_.substr(0, caret_pos_);
364      if (!composition_.empty())
365        str += composition_.substr(0, composition_selection_.first);
366      int px = font_.MeasureSimpleText(str);
367      pp::Rect caret(area_.x() + px, area_.y(), 0, area_.height() + 2);
368      status_handler_->FocusIn(caret);
369      status_handler_->UpdateSelection(
370          utf8_text_.substr(SelectionLeft(),
371                            SelectionRight() - SelectionLeft()));
372    }
373  }
374  size_t SelectionLeft() const {
375    return std::min(caret_pos_, anchor_pos_);
376  }
377  size_t SelectionRight() const {
378    return std::max(caret_pos_, anchor_pos_);
379  }
380  bool HasSelection() const {
381    return caret_pos_ != anchor_pos_;
382  }
383
384  pp::Instance* instance_;
385  TextFieldStatusHandler* status_handler_;
386
387  pp::Rect area_;
388  int font_size_;
389  pp::Font_Dev font_;
390  std::string utf8_text_;
391  size_t caret_pos_;
392  size_t anchor_pos_;
393  std::string composition_;
394  std::vector< std::pair<uint32_t, uint32_t> > segments_;
395  std::pair<uint32_t, uint32_t> composition_selection_;
396  int target_segment_;
397};
398
399class MyInstance : public pp::Instance {
400 public:
401  explicit MyInstance(PP_Instance instance)
402      : pp::Instance(instance),
403        status_handler_(new TextFieldStatusHandler),
404        dragging_(false) {
405  }
406
407  ~MyInstance() {
408    delete status_handler_;
409  }
410
411  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
412    RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
413    RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
414
415    for (uint32_t i = 0; i < argc; ++i) {
416      if (argn[i] == std::string("ime")) {
417        if (argv[i] == std::string("no")) {
418          // Example of NO-IME plugins (e.g., games).
419          //
420          // When a plugin never wants to accept text input, at initialization
421          // explicitly turn off the text input feature by calling:
422          pp::TextInputController(this).SetTextInputType(
423              PP_TEXTINPUT_TYPE_NONE);
424        } else if (argv[i] == std::string("unaware")) {
425          // Demonstrating the behavior of IME-unaware plugins.
426          // Never call any text input related APIs.
427          //
428          // In such a case, the plugin is assumed to always accept text input.
429          // For example, when the plugin is focused in touch devices a virtual
430          // keyboard may pop up, or in environment IME is used, users can type
431          // text via IME on the plugin. The characters are delivered to the
432          // plugin via PP_INPUTEVENT_TYPE_CHAR events.
433        } else if (argv[i] == std::string("caretmove")) {
434          // Demonstrating the behavior of plugins with limited IME support.
435          //
436          // It uses SetTextInputType() and UpdateCaretPosition() API to notify
437          // text input status to the browser, but unable to handle inline
438          // compositions. By using the notified information. the browser can,
439          // say, show virtual keyboards or IMEs only at appropriate timing
440          // that the plugin does need to accept text input.
441          delete status_handler_;
442          status_handler_ = new TextFieldStatusNotifyingHandler(this);
443        } else if (argv[i] == std::string("full")) {
444          // Demonstrating the behavior of plugins fully supporting IME.
445          //
446          // It notifies updates of caret positions to the browser,
447          // and handles all text input events by itself.
448          delete status_handler_;
449          status_handler_ = new TextFieldStatusNotifyingHandler(this);
450          RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
451        }
452        break;
453      }
454    }
455
456    textfield_.push_back(MyTextField(this, status_handler_,
457                                     10, 10, 300, 20));
458    textfield_.back().SetText("Hello");
459    textfield_.push_back(MyTextField(this, status_handler_,
460                                     30, 100, 300, 20));
461    textfield_.back().SetText("World");
462    return true;
463  }
464
465 protected:
466  virtual bool HandleInputEvent(const pp::InputEvent& event) {
467    bool ret = false;
468    switch (event.GetType()) {
469      case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
470        const pp::MouseInputEvent mouseEvent(event);
471        ret = OnMouseDown(mouseEvent);
472        break;
473      }
474      case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
475        const pp::MouseInputEvent mouseEvent(event);
476        ret = OnMouseMove(mouseEvent);
477        break;
478      }
479      case PP_INPUTEVENT_TYPE_MOUSEUP: {
480        const pp::MouseInputEvent mouseEvent(event);
481        ret = OnMouseUp(mouseEvent);
482        break;
483      }
484      case PP_INPUTEVENT_TYPE_KEYDOWN: {
485        Log("Keydown");
486        const pp::KeyboardInputEvent keyEvent(event);
487        ret = OnKeyDown(keyEvent);
488        break;
489      }
490      case PP_INPUTEVENT_TYPE_CHAR: {
491        const pp::KeyboardInputEvent keyEvent(event);
492        Log("Char [" + keyEvent.GetCharacterText().AsString() + "]");
493        ret = OnChar(keyEvent);
494        break;
495      }
496      case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: {
497        const pp::IMEInputEvent imeEvent(event);
498        Log("CompositionStart [" + imeEvent.GetText().AsString() + "]");
499        ret = true;
500        break;
501      }
502      case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: {
503        const pp::IMEInputEvent imeEvent(event);
504        Log("CompositionUpdate [" + imeEvent.GetText().AsString() + "]");
505        ret = OnCompositionUpdate(imeEvent);
506        break;
507      }
508      case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: {
509        const pp::IMEInputEvent imeEvent(event);
510        Log("CompositionEnd [" + imeEvent.GetText().AsString() + "]");
511        ret = OnCompositionEnd(imeEvent);
512        break;
513      }
514      case PP_INPUTEVENT_TYPE_IME_TEXT: {
515        const pp::IMEInputEvent imeEvent(event);
516        Log("ImeText [" + imeEvent.GetText().AsString() + "]");
517        ret = OnImeText(imeEvent);
518        break;
519      }
520      default:
521        break;
522    }
523    if (ret && (dragging_ || event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE))
524      Paint();
525    return ret;
526  }
527
528  virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
529    if (position.size() == last_size_)
530      return;
531    last_size_ = position.size();
532    Paint();
533  }
534
535 private:
536  bool OnCompositionUpdate(const pp::IMEInputEvent& ev) {
537    for (std::vector<MyTextField>::iterator it = textfield_.begin();
538         it != textfield_.end();
539         ++it) {
540      if (it->Focused()) {
541        std::vector< std::pair<uint32_t, uint32_t> > segs;
542        for (uint32_t i = 0; i < ev.GetSegmentNumber(); ++i)
543          segs.push_back(std::make_pair(ev.GetSegmentOffset(i),
544                                        ev.GetSegmentOffset(i + 1)));
545        uint32_t selection_start;
546        uint32_t selection_end;
547        ev.GetSelection(&selection_start, &selection_end);
548        it->SetComposition(ev.GetText().AsString(),
549                           segs,
550                           ev.GetTargetSegment(),
551                           std::make_pair(selection_start, selection_end));
552        return true;
553      }
554    }
555    return false;
556  }
557
558  bool OnCompositionEnd(const pp::IMEInputEvent& ev) {
559    for (std::vector<MyTextField>::iterator it = textfield_.begin();
560         it != textfield_.end();
561         ++it) {
562      if (it->Focused()) {
563        it->SetComposition(std::string(),
564                           std::vector<std::pair<uint32_t, uint32_t> >(),
565                           0,
566                           std::make_pair(0, 0));
567        return true;
568      }
569    }
570    return false;
571  }
572
573  bool OnMouseDown(const pp::MouseInputEvent& ev) {
574    dragging_ = true;
575
576    bool anyone_focused = false;
577    for (std::vector<MyTextField>::iterator it = textfield_.begin();
578         it != textfield_.end();
579         ++it) {
580      if (it->RefocusByMouseClick(ev.GetPosition().x(),
581                                  ev.GetPosition().y())) {
582        anyone_focused = true;
583      }
584    }
585    if (!anyone_focused)
586      status_handler_->FocusOut();
587    return true;
588  }
589
590  bool OnMouseMove(const pp::MouseInputEvent& ev) {
591    const PPB_CursorControl_Dev* cursor_control =
592        reinterpret_cast<const PPB_CursorControl_Dev*>(
593            pp::Module::Get()->GetBrowserInterface(
594                PPB_CURSOR_CONTROL_DEV_INTERFACE));
595    if (!cursor_control)
596      return false;
597
598    for (std::vector<MyTextField>::iterator it = textfield_.begin();
599         it != textfield_.end();
600         ++it) {
601      if (it->Contains(ev.GetPosition().x(),
602                       ev.GetPosition().y())) {
603        cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
604                                  0, NULL);
605        if (it->Focused() && dragging_)
606          it->MouseDrag(ev.GetPosition().x(), ev.GetPosition().y());
607        return true;
608      }
609    }
610    cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
611                              0, NULL);
612    return true;
613  }
614
615  bool OnMouseUp(const pp::MouseInputEvent& ev) {
616    dragging_ = false;
617    for (std::vector<MyTextField>::iterator it = textfield_.begin();
618         it != textfield_.end();
619         ++it)
620      if (it->Focused())
621        it->MouseUp(ev.GetPosition().x(), ev.GetPosition().y());
622    return false;
623  }
624
625  bool OnKeyDown(const pp::KeyboardInputEvent& ev) {
626    for (std::vector<MyTextField>::iterator it = textfield_.begin();
627         it != textfield_.end();
628         ++it) {
629      if (it->Focused()) {
630        bool shift = ev.GetModifiers() & PP_INPUTEVENT_MODIFIER_SHIFTKEY;
631        switch (ev.GetKeyCode()) {
632          case VKEY_LEFT:
633            it->KeyLeft(shift);
634            break;
635          case VKEY_RIGHT:
636            it->KeyRight(shift);
637            break;
638          case VKEY_DELETE:
639            it->KeyDelete();
640            break;
641          case VKEY_BACK:
642            it->KeyBackspace();
643            break;
644        }
645        return true;
646      }
647    }
648    return false;
649  }
650
651  bool OnChar(const pp::KeyboardInputEvent& ev) {
652    for (std::vector<MyTextField>::iterator it = textfield_.begin();
653         it != textfield_.end();
654         ++it) {
655      if (it->Focused()) {
656        std::string str = ev.GetCharacterText().AsString();
657        if (str != "\r" && str != "\n")
658          it->InsertText(str);
659        return true;
660      }
661    }
662    return false;
663  }
664
665  bool OnImeText(const pp::IMEInputEvent ev) {
666    for (std::vector<MyTextField>::iterator it = textfield_.begin();
667         it != textfield_.end();
668         ++it) {
669      if (it->Focused()) {
670        it->InsertText(ev.GetText().AsString());
671        return true;
672      }
673    }
674    return false;
675  }
676
677  void Paint() {
678    pp::Rect clip(0, 0, last_size_.width(), last_size_.height());
679    PaintClip(clip);
680  }
681
682  void PaintClip(const pp::Rect& clip) {
683    pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_, true);
684    pp::Graphics2D device(this, last_size_, false);
685    BindGraphics(device);
686
687    for (std::vector<MyTextField>::iterator it = textfield_.begin();
688         it != textfield_.end();
689         ++it) {
690      it->PaintOn(&image, clip);
691    }
692
693    device.PaintImageData(image, pp::Point(0, 0));
694    device.Flush(pp::CompletionCallback(&OnFlush, this));
695  }
696
697  static void OnFlush(void* user_data, int32_t result) {}
698
699  // Prints a debug message.
700  void Log(const pp::Var& value) {
701    const PPB_Console* console = reinterpret_cast<const PPB_Console*>(
702        pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
703    if (!console)
704      return;
705    console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
706  }
707
708  // IME Control interface.
709  TextFieldStatusHandler* status_handler_;
710
711  // Remembers the size of this instance.
712  pp::Size last_size_;
713
714  // Holds instances of text fields.
715  std::vector<MyTextField> textfield_;
716
717  // Whether or not during a drag operation.
718  bool dragging_;
719};
720
721class MyModule : public pp::Module {
722  virtual pp::Instance* CreateInstance(PP_Instance instance) {
723    return new MyInstance(instance);
724  }
725};
726
727namespace pp {
728
729Module* CreateModule() {
730  return new MyModule();
731}
732
733}  // namespace pp
734