Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / ppapi / examples / ime / ime.cc
blob805d14052f541115731015b214395f1210161878
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(
116 text, 0, static_cast<uint32_t>(text.size()));
119 private:
120 pp::TextInputController textinput_control_;
123 // Hand-made text field for demonstrating text input API.
124 class MyTextField {
125 public:
126 MyTextField(pp::Instance* instance, TextFieldStatusHandler* handler,
127 int x, int y, int width, int height)
128 : instance_(instance),
129 status_handler_(handler),
130 area_(x, y, width, height),
131 font_size_(height - 2),
132 caret_pos_(std::string::npos),
133 anchor_pos_(std::string::npos),
134 target_segment_(0) {
135 pp::FontDescription_Dev desc;
136 desc.set_family(PP_FONTFAMILY_SANSSERIF);
137 desc.set_size(font_size_);
138 font_ = pp::Font_Dev(instance_, desc);
141 // Paint on the specified ImageData.
142 void PaintOn(pp::ImageData* image, pp::Rect clip) {
143 clip = clip.Intersect(area_);
144 FillRect(image, clip, kTextfieldBgColor);
146 if (caret_pos_ != std::string::npos) {
147 int offset = area_.x();
148 // selection (for the case without composition text)
149 if (composition_.empty() && HasSelection()) {
150 int left_x = font_.MeasureSimpleText(
151 utf8_text_.substr(0, SelectionLeft()));
152 int right_x = font_.MeasureSimpleText(
153 utf8_text_.substr(0, SelectionRight()));
154 FillRect(image, offset + left_x, area_.y(), right_x - left_x,
155 area_.height(), kTextfieldSelectionBackgroundColor);
157 // before caret
159 std::string str = utf8_text_.substr(0, caret_pos_);
160 font_.DrawTextAt(
161 image,
162 pp::TextRun_Dev(str.c_str(), false, false),
163 pp::Point(offset, area_.y() + font_size_),
164 kTextfieldTextColor,
165 clip,
166 false);
167 offset += font_.MeasureSimpleText(str);
169 // composition
171 const std::string& str = composition_;
172 // selection
173 if (composition_selection_.first != composition_selection_.second) {
174 int left_x = font_.MeasureSimpleText(
175 str.substr(0, composition_selection_.first));
176 int right_x = font_.MeasureSimpleText(
177 str.substr(0, composition_selection_.second));
178 FillRect(image, offset + left_x, area_.y(), right_x - left_x,
179 area_.height(), kTextfieldSelectionBackgroundColor);
181 // composition text
182 font_.DrawTextAt(
183 image,
184 pp::TextRun_Dev(str.c_str(), false, false),
185 pp::Point(offset, area_.y() + font_size_),
186 kTextfieldPreeditTextColor,
187 clip,
188 false);
189 for (size_t i = 0; i < segments_.size(); ++i) {
190 size_t l = segments_[i].first;
191 size_t r = segments_[i].second;
192 if (l != r) {
193 int lx = font_.MeasureSimpleText(str.substr(0, l));
194 int rx = font_.MeasureSimpleText(str.substr(0, r));
195 FillRect(image,
196 offset + lx + 2, area_.y() + font_size_ + 1,
197 rx - lx - 4, 2,
198 i == static_cast<size_t>(target_segment_) ?
199 kTextfieldUnderlineColorMain :
200 kTextfieldUnderlineColorSub);
203 // caret
204 int caretx = font_.MeasureSimpleText(
205 str.substr(0, composition_selection_.first));
206 FillRect(image,
207 pp::Rect(offset + caretx, area_.y(), 2, area_.height()),
208 kTextfieldCaretColor);
209 offset += font_.MeasureSimpleText(str);
211 // after caret
213 std::string str = utf8_text_.substr(caret_pos_);
214 font_.DrawTextAt(
215 image,
216 pp::TextRun_Dev(str.c_str(), false, false),
217 pp::Point(offset, area_.y() + font_size_),
218 kTextfieldTextColor,
219 clip,
220 false);
222 } else {
223 font_.DrawTextAt(
224 image,
225 pp::TextRun_Dev(utf8_text_.c_str(), false, false),
226 pp::Point(area_.x(), area_.y() + font_size_),
227 kTextfieldTextColor,
228 clip,
229 false);
233 // Update current composition text.
234 void SetComposition(
235 const std::string& text,
236 const std::vector< std::pair<uint32_t, uint32_t> >& segments,
237 int32_t target_segment,
238 const std::pair<uint32_t, uint32_t>& selection) {
239 if (HasSelection() && !text.empty())
240 InsertText(std::string());
241 composition_ = text;
242 segments_ = segments;
243 target_segment_ = target_segment;
244 composition_selection_ = selection;
245 CaretPosChanged();
248 // Is the text field focused?
249 bool Focused() const {
250 return caret_pos_ != std::string::npos;
253 // Does the coordinate (x,y) is contained inside the edit box?
254 bool Contains(int x, int y) const {
255 return area_.Contains(x, y);
258 // Resets the content text.
259 void SetText(const std::string& text) {
260 utf8_text_ = text;
261 if (Focused()) {
262 caret_pos_ = anchor_pos_ = text.size();
263 CaretPosChanged();
267 // Inserts a text at the current caret position.
268 void InsertText(const std::string& text) {
269 if (!Focused())
270 return;
271 utf8_text_.replace(SelectionLeft(), SelectionRight() - SelectionLeft(),
272 text);
273 caret_pos_ = anchor_pos_ = SelectionLeft() + text.size();
274 CaretPosChanged();
277 // Handles mouse click event and changes the focus state.
278 bool RefocusByMouseClick(int x, int y) {
279 if (!Contains(x, y)) {
280 // The text field is unfocused.
281 caret_pos_ = anchor_pos_ = std::string::npos;
282 return false;
285 // The text field is focused.
286 size_t n = font_.CharacterOffsetForPixel(
287 pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
288 caret_pos_ = anchor_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
289 CaretPosChanged();
290 return true;
293 void MouseDrag(int x, int y) {
294 if (!Focused())
295 return;
296 size_t n = font_.CharacterOffsetForPixel(
297 pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
298 caret_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
301 void MouseUp(int x, int y) {
302 if (!Focused())
303 return;
304 CaretPosChanged();
307 void KeyLeft(bool shift) {
308 if (!Focused())
309 return;
310 // Move caret to the head of the selection or to the previous character.
311 if (!shift && HasSelection())
312 caret_pos_ = SelectionLeft();
313 else
314 caret_pos_ = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
315 // Move the anchor if the shift key is not pressed.
316 if (!shift)
317 anchor_pos_ = caret_pos_;
318 CaretPosChanged();
321 void KeyRight(bool shift) {
322 if (!Focused())
323 return;
324 // Move caret to the end of the selection or to the next character.
325 if (!shift && HasSelection())
326 caret_pos_ = SelectionRight();
327 else
328 caret_pos_ = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
329 // Move the anchor if the shift key is not pressed.
330 if (!shift)
331 anchor_pos_ = caret_pos_;
332 CaretPosChanged();
335 void KeyDelete() {
336 if (!Focused())
337 return;
338 if (HasSelection()) {
339 InsertText(std::string());
340 } else {
341 size_t i = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
342 utf8_text_.erase(caret_pos_, i - caret_pos_);
343 CaretPosChanged();
347 void KeyBackspace() {
348 if (!Focused())
349 return;
350 if (HasSelection()) {
351 InsertText(std::string());
352 } else if (caret_pos_ != 0) {
353 size_t i = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
354 utf8_text_.erase(i, caret_pos_ - i);
355 caret_pos_ = anchor_pos_ = i;
356 CaretPosChanged();
360 private:
361 // Notify the plugin instance that the caret position has changed.
362 void CaretPosChanged() {
363 if (Focused()) {
364 std::string str = utf8_text_.substr(0, caret_pos_);
365 if (!composition_.empty())
366 str += composition_.substr(0, composition_selection_.first);
367 int px = font_.MeasureSimpleText(str);
368 pp::Rect caret(area_.x() + px, area_.y(), 0, area_.height() + 2);
369 status_handler_->FocusIn(caret);
370 status_handler_->UpdateSelection(
371 utf8_text_.substr(SelectionLeft(),
372 SelectionRight() - SelectionLeft()));
375 size_t SelectionLeft() const {
376 return std::min(caret_pos_, anchor_pos_);
378 size_t SelectionRight() const {
379 return std::max(caret_pos_, anchor_pos_);
381 bool HasSelection() const {
382 return caret_pos_ != anchor_pos_;
385 pp::Instance* instance_;
386 TextFieldStatusHandler* status_handler_;
388 pp::Rect area_;
389 int font_size_;
390 pp::Font_Dev font_;
391 std::string utf8_text_;
392 size_t caret_pos_;
393 size_t anchor_pos_;
394 std::string composition_;
395 std::vector< std::pair<uint32_t, uint32_t> > segments_;
396 std::pair<uint32_t, uint32_t> composition_selection_;
397 int target_segment_;
400 class MyInstance : public pp::Instance {
401 public:
402 explicit MyInstance(PP_Instance instance)
403 : pp::Instance(instance),
404 status_handler_(new TextFieldStatusHandler),
405 dragging_(false) {
408 ~MyInstance() {
409 delete status_handler_;
412 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
413 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
414 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
416 for (uint32_t i = 0; i < argc; ++i) {
417 if (argn[i] == std::string("ime")) {
418 if (argv[i] == std::string("no")) {
419 // Example of NO-IME plugins (e.g., games).
421 // When a plugin never wants to accept text input, at initialization
422 // explicitly turn off the text input feature by calling:
423 pp::TextInputController(this).SetTextInputType(
424 PP_TEXTINPUT_TYPE_NONE);
425 } else if (argv[i] == std::string("unaware")) {
426 // Demonstrating the behavior of IME-unaware plugins.
427 // Never call any text input related APIs.
429 // In such a case, the plugin is assumed to always accept text input.
430 // For example, when the plugin is focused in touch devices a virtual
431 // keyboard may pop up, or in environment IME is used, users can type
432 // text via IME on the plugin. The characters are delivered to the
433 // plugin via PP_INPUTEVENT_TYPE_CHAR events.
434 } else if (argv[i] == std::string("caretmove")) {
435 // Demonstrating the behavior of plugins with limited IME support.
437 // It uses SetTextInputType() and UpdateCaretPosition() API to notify
438 // text input status to the browser, but unable to handle inline
439 // compositions. By using the notified information. the browser can,
440 // say, show virtual keyboards or IMEs only at appropriate timing
441 // that the plugin does need to accept text input.
442 delete status_handler_;
443 status_handler_ = new TextFieldStatusNotifyingHandler(this);
444 } else if (argv[i] == std::string("full")) {
445 // Demonstrating the behavior of plugins fully supporting IME.
447 // It notifies updates of caret positions to the browser,
448 // and handles all text input events by itself.
449 delete status_handler_;
450 status_handler_ = new TextFieldStatusNotifyingHandler(this);
451 RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
453 break;
457 textfield_.push_back(MyTextField(this, status_handler_,
458 10, 10, 300, 20));
459 textfield_.back().SetText("Hello");
460 textfield_.push_back(MyTextField(this, status_handler_,
461 30, 100, 300, 20));
462 textfield_.back().SetText("World");
463 return true;
466 protected:
467 virtual bool HandleInputEvent(const pp::InputEvent& event) {
468 bool ret = false;
469 switch (event.GetType()) {
470 case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
471 const pp::MouseInputEvent mouseEvent(event);
472 ret = OnMouseDown(mouseEvent);
473 break;
475 case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
476 const pp::MouseInputEvent mouseEvent(event);
477 ret = OnMouseMove(mouseEvent);
478 break;
480 case PP_INPUTEVENT_TYPE_MOUSEUP: {
481 const pp::MouseInputEvent mouseEvent(event);
482 ret = OnMouseUp(mouseEvent);
483 break;
485 case PP_INPUTEVENT_TYPE_KEYDOWN: {
486 Log("Keydown");
487 const pp::KeyboardInputEvent keyEvent(event);
488 ret = OnKeyDown(keyEvent);
489 break;
491 case PP_INPUTEVENT_TYPE_CHAR: {
492 const pp::KeyboardInputEvent keyEvent(event);
493 Log("Char [" + keyEvent.GetCharacterText().AsString() + "]");
494 ret = OnChar(keyEvent);
495 break;
497 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: {
498 const pp::IMEInputEvent imeEvent(event);
499 Log("CompositionStart [" + imeEvent.GetText().AsString() + "]");
500 ret = true;
501 break;
503 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: {
504 const pp::IMEInputEvent imeEvent(event);
505 Log("CompositionUpdate [" + imeEvent.GetText().AsString() + "]");
506 ret = OnCompositionUpdate(imeEvent);
507 break;
509 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: {
510 const pp::IMEInputEvent imeEvent(event);
511 Log("CompositionEnd [" + imeEvent.GetText().AsString() + "]");
512 ret = OnCompositionEnd(imeEvent);
513 break;
515 case PP_INPUTEVENT_TYPE_IME_TEXT: {
516 const pp::IMEInputEvent imeEvent(event);
517 Log("ImeText [" + imeEvent.GetText().AsString() + "]");
518 ret = OnImeText(imeEvent);
519 break;
521 default:
522 break;
524 if (ret && (dragging_ || event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE))
525 Paint();
526 return ret;
529 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
530 if (position.size() == last_size_)
531 return;
532 last_size_ = position.size();
533 Paint();
536 private:
537 bool OnCompositionUpdate(const pp::IMEInputEvent& ev) {
538 for (std::vector<MyTextField>::iterator it = textfield_.begin();
539 it != textfield_.end();
540 ++it) {
541 if (it->Focused()) {
542 std::vector< std::pair<uint32_t, uint32_t> > segs;
543 for (uint32_t i = 0; i < ev.GetSegmentNumber(); ++i)
544 segs.push_back(std::make_pair(ev.GetSegmentOffset(i),
545 ev.GetSegmentOffset(i + 1)));
546 uint32_t selection_start;
547 uint32_t selection_end;
548 ev.GetSelection(&selection_start, &selection_end);
549 it->SetComposition(ev.GetText().AsString(),
550 segs,
551 ev.GetTargetSegment(),
552 std::make_pair(selection_start, selection_end));
553 return true;
556 return false;
559 bool OnCompositionEnd(const pp::IMEInputEvent& ev) {
560 for (std::vector<MyTextField>::iterator it = textfield_.begin();
561 it != textfield_.end();
562 ++it) {
563 if (it->Focused()) {
564 it->SetComposition(std::string(),
565 std::vector<std::pair<uint32_t, uint32_t> >(),
567 std::make_pair(0, 0));
568 return true;
571 return false;
574 bool OnMouseDown(const pp::MouseInputEvent& ev) {
575 dragging_ = true;
577 bool anyone_focused = false;
578 for (std::vector<MyTextField>::iterator it = textfield_.begin();
579 it != textfield_.end();
580 ++it) {
581 if (it->RefocusByMouseClick(ev.GetPosition().x(),
582 ev.GetPosition().y())) {
583 anyone_focused = true;
586 if (!anyone_focused)
587 status_handler_->FocusOut();
588 return true;
591 bool OnMouseMove(const pp::MouseInputEvent& ev) {
592 const PPB_CursorControl_Dev* cursor_control =
593 reinterpret_cast<const PPB_CursorControl_Dev*>(
594 pp::Module::Get()->GetBrowserInterface(
595 PPB_CURSOR_CONTROL_DEV_INTERFACE));
596 if (!cursor_control)
597 return false;
599 for (std::vector<MyTextField>::iterator it = textfield_.begin();
600 it != textfield_.end();
601 ++it) {
602 if (it->Contains(ev.GetPosition().x(),
603 ev.GetPosition().y())) {
604 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
605 0, NULL);
606 if (it->Focused() && dragging_)
607 it->MouseDrag(ev.GetPosition().x(), ev.GetPosition().y());
608 return true;
611 cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
612 0, NULL);
613 return true;
616 bool OnMouseUp(const pp::MouseInputEvent& ev) {
617 dragging_ = false;
618 for (std::vector<MyTextField>::iterator it = textfield_.begin();
619 it != textfield_.end();
620 ++it)
621 if (it->Focused())
622 it->MouseUp(ev.GetPosition().x(), ev.GetPosition().y());
623 return false;
626 bool OnKeyDown(const pp::KeyboardInputEvent& ev) {
627 for (std::vector<MyTextField>::iterator it = textfield_.begin();
628 it != textfield_.end();
629 ++it) {
630 if (it->Focused()) {
631 bool shift = ev.GetModifiers() & PP_INPUTEVENT_MODIFIER_SHIFTKEY;
632 switch (ev.GetKeyCode()) {
633 case VKEY_LEFT:
634 it->KeyLeft(shift);
635 break;
636 case VKEY_RIGHT:
637 it->KeyRight(shift);
638 break;
639 case VKEY_DELETE:
640 it->KeyDelete();
641 break;
642 case VKEY_BACK:
643 it->KeyBackspace();
644 break;
646 return true;
649 return false;
652 bool OnChar(const pp::KeyboardInputEvent& ev) {
653 for (std::vector<MyTextField>::iterator it = textfield_.begin();
654 it != textfield_.end();
655 ++it) {
656 if (it->Focused()) {
657 std::string str = ev.GetCharacterText().AsString();
658 if (str != "\r" && str != "\n")
659 it->InsertText(str);
660 return true;
663 return false;
666 bool OnImeText(const pp::IMEInputEvent ev) {
667 for (std::vector<MyTextField>::iterator it = textfield_.begin();
668 it != textfield_.end();
669 ++it) {
670 if (it->Focused()) {
671 it->InsertText(ev.GetText().AsString());
672 return true;
675 return false;
678 void Paint() {
679 pp::Rect clip(0, 0, last_size_.width(), last_size_.height());
680 PaintClip(clip);
683 void PaintClip(const pp::Rect& clip) {
684 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_, true);
685 pp::Graphics2D device(this, last_size_, false);
686 BindGraphics(device);
688 for (std::vector<MyTextField>::iterator it = textfield_.begin();
689 it != textfield_.end();
690 ++it) {
691 it->PaintOn(&image, clip);
694 device.PaintImageData(image, pp::Point(0, 0));
695 device.Flush(pp::CompletionCallback(&OnFlush, this));
698 static void OnFlush(void* user_data, int32_t result) {}
700 // Prints a debug message.
701 void Log(const pp::Var& value) {
702 const PPB_Console* console = reinterpret_cast<const PPB_Console*>(
703 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
704 if (!console)
705 return;
706 console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
709 // IME Control interface.
710 TextFieldStatusHandler* status_handler_;
712 // Remembers the size of this instance.
713 pp::Size last_size_;
715 // Holds instances of text fields.
716 std::vector<MyTextField> textfield_;
718 // Whether or not during a drag operation.
719 bool dragging_;
722 class MyModule : public pp::Module {
723 virtual pp::Instance* CreateInstance(PP_Instance instance) {
724 return new MyInstance(instance);
728 namespace pp {
730 Module* CreateModule() {
731 return new MyModule();
734 } // namespace pp