Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ppapi / examples / ime / ime.cc
blob26c2f4225a9bad2e8c5d73dcf1264fc9c7c8a55d
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.
5 #include <algorithm>
6 #include <string>
7 #include <utility>
8 #include <vector>
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"
23 namespace {
25 // Extracted from: ui/events/keycodes/keyboard_codes.h
26 enum {
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,
36 const uint32_t kTextfieldBgColor = 0xffffffff;
37 const uint32_t kTextfieldTextColor = 0xff000000;
38 const uint32_t kTextfieldCaretColor = 0xff000000;
39 const uint32_t kTextfieldPreeditTextColor = 0xffff0000;
40 const uint32_t kTextfieldSelectionBackgroundColor = 0xffeecccc;
41 const uint32_t kTextfieldUnderlineColorMain = 0xffff0000;
42 const uint32_t kTextfieldUnderlineColorSub = 0xffddaaaa;
44 void 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;
57 void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
58 FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
61 size_t GetPrevCharOffsetUtf8(const std::string& str, size_t current_pos) {
62 size_t i = current_pos;
63 if (i > 0) {
65 --i;
66 while (i > 0 && (str[i] & 0xc0) == 0x80);
68 return i;
71 size_t GetNextCharOffsetUtf8(const std::string& str, size_t current_pos) {
72 size_t i = current_pos;
73 if (i < str.size()) {
75 ++i;
76 while (i < str.size() && (str[i] & 0xc0) == 0x80);
78 return i;
81 size_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;
88 } // namespace
90 class 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) {}
98 class TextFieldStatusNotifyingHandler : public TextFieldStatusHandler {
99 public:
100 explicit TextFieldStatusNotifyingHandler(pp::Instance* instance)
101 : textinput_control_(instance) {
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);
110 virtual void FocusOut() {
111 textinput_control_.CancelCompositionText();
112 textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
114 virtual void UpdateSelection(const std::string& text) {
115 textinput_control_.UpdateSurroundingText(text, 0, text.size());
118 private:
119 pp::TextInputController textinput_control_;
122 // Hand-made text field for demonstrating text input API.
123 class 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);
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);
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);
156 // before caret
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);
168 // composition
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);
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);
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);
210 // after caret
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);
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);
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();
247 // Is the text field focused?
248 bool Focused() const {
249 return caret_pos_ != std::string::npos;
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);
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();
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();
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;
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;
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);
300 void MouseUp(int x, int y) {
301 if (!Focused())
302 return;
303 CaretPosChanged();
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();
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();
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();
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();
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()));
374 size_t SelectionLeft() const {
375 return std::min(caret_pos_, anchor_pos_);
377 size_t SelectionRight() const {
378 return std::max(caret_pos_, anchor_pos_);
380 bool HasSelection() const {
381 return caret_pos_ != anchor_pos_;
384 pp::Instance* instance_;
385 TextFieldStatusHandler* status_handler_;
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_;
399 class MyInstance : public pp::Instance {
400 public:
401 explicit MyInstance(PP_Instance instance)
402 : pp::Instance(instance),
403 status_handler_(new TextFieldStatusHandler),
404 dragging_(false) {
407 ~MyInstance() {
408 delete status_handler_;
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);
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).
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.
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.
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.
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);
452 break;
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;
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;
474 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
475 const pp::MouseInputEvent mouseEvent(event);
476 ret = OnMouseMove(mouseEvent);
477 break;
479 case PP_INPUTEVENT_TYPE_MOUSEUP: {
480 const pp::MouseInputEvent mouseEvent(event);
481 ret = OnMouseUp(mouseEvent);
482 break;
484 case PP_INPUTEVENT_TYPE_KEYDOWN: {
485 Log("Keydown");
486 const pp::KeyboardInputEvent keyEvent(event);
487 ret = OnKeyDown(keyEvent);
488 break;
490 case PP_INPUTEVENT_TYPE_CHAR: {
491 const pp::KeyboardInputEvent keyEvent(event);
492 Log("Char [" + keyEvent.GetCharacterText().AsString() + "]");
493 ret = OnChar(keyEvent);
494 break;
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;
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;
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;
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;
520 default:
521 break;
523 if (ret && (dragging_ || event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE))
524 Paint();
525 return ret;
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();
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;
555 return false;
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> >(),
566 std::make_pair(0, 0));
567 return true;
570 return false;
573 bool OnMouseDown(const pp::MouseInputEvent& ev) {
574 dragging_ = true;
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;
585 if (!anyone_focused)
586 status_handler_->FocusOut();
587 return true;
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;
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;
610 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
611 0, NULL);
612 return true;
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;
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;
645 return true;
648 return false;
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;
662 return false;
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;
674 return false;
677 void Paint() {
678 pp::Rect clip(0, 0, last_size_.width(), last_size_.height());
679 PaintClip(clip);
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);
687 for (std::vector<MyTextField>::iterator it = textfield_.begin();
688 it != textfield_.end();
689 ++it) {
690 it->PaintOn(&image, clip);
693 device.PaintImageData(image, pp::Point(0, 0));
694 device.Flush(pp::CompletionCallback(&OnFlush, this));
697 static void OnFlush(void* user_data, int32_t result) {}
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());
708 // IME Control interface.
709 TextFieldStatusHandler* status_handler_;
711 // Remembers the size of this instance.
712 pp::Size last_size_;
714 // Holds instances of text fields.
715 std::vector<MyTextField> textfield_;
717 // Whether or not during a drag operation.
718 bool dragging_;
721 class MyModule : public pp::Module {
722 virtual pp::Instance* CreateInstance(PP_Instance instance) {
723 return new MyInstance(instance);
727 namespace pp {
729 Module* CreateModule() {
730 return new MyModule();
733 } // namespace pp