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